| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882388338843885388638873888388938903891389238933894389538963897389838993900390139023903390439053906390739083909391039113912391339143915391639173918391939203921392239233924392539263927392839293930393139323933393439353936393739383939394039413942394339443945394639473948394939503951395239533954395539563957395839593960396139623963396439653966396739683969397039713972397339743975397639773978397939803981398239833984398539863987398839893990399139923993399439953996399739983999400040014002400340044005400640074008400940104011401240134014401540164017401840194020402140224023402440254026402740284029403040314032403340344035403640374038403940404041404240434044404540464047404840494050405140524053405440554056405740584059406040614062406340644065406640674068406940704071407240734074407540764077407840794080408140824083408440854086408740884089409040914092409340944095409640974098409941004101410241034104410541064107410841094110411141124113411441154116411741184119412041214122412341244125412641274128412941304131413241334134413541364137413841394140414141424143414441454146414741484149415041514152415341544155415641574158415941604161416241634164416541664167416841694170417141724173417441754176417741784179418041814182418341844185418641874188418941904191419241934194419541964197419841994200420142024203420442054206420742084209421042114212421342144215421642174218421942204221422242234224422542264227422842294230423142324233423442354236423742384239424042414242424342444245424642474248424942504251425242534254425542564257425842594260426142624263426442654266426742684269427042714272427342744275427642774278427942804281428242834284428542864287428842894290429142924293429442954296429742984299430043014302430343044305430643074308430943104311431243134314431543164317431843194320432143224323432443254326432743284329433043314332433343344335433643374338433943404341434243434344434543464347434843494350435143524353435443554356435743584359436043614362436343644365436643674368436943704371437243734374437543764377437843794380438143824383438443854386438743884389439043914392439343944395439643974398439944004401440244034404440544064407440844094410441144124413441444154416441744184419442044214422442344244425442644274428442944304431443244334434443544364437443844394440444144424443444444454446444744484449445044514452445344544455445644574458445944604461446244634464446544664467446844694470447144724473447444754476447744784479448044814482448344844485448644874488448944904491449244934494449544964497449844994500450145024503450445054506450745084509451045114512451345144515451645174518451945204521452245234524452545264527452845294530453145324533453445354536453745384539454045414542454345444545454645474548454945504551455245534554455545564557455845594560456145624563456445654566456745684569457045714572457345744575457645774578457945804581458245834584458545864587458845894590459145924593459445954596459745984599460046014602460346044605460646074608460946104611461246134614461546164617461846194620462146224623462446254626462746284629463046314632463346344635463646374638463946404641464246434644464546464647464846494650465146524653465446554656465746584659466046614662466346644665466646674668466946704671467246734674467546764677467846794680468146824683468446854686468746884689469046914692469346944695469646974698469947004701470247034704470547064707470847094710471147124713471447154716471747184719472047214722472347244725472647274728472947304731473247334734473547364737473847394740474147424743474447454746474747484749475047514752475347544755475647574758475947604761476247634764476547664767476847694770477147724773477447754776477747784779478047814782478347844785478647874788478947904791479247934794479547964797479847994800480148024803480448054806480748084809481048114812481348144815481648174818481948204821482248234824482548264827482848294830483148324833483448354836483748384839484048414842484348444845484648474848484948504851485248534854485548564857485848594860486148624863486448654866486748684869487048714872487348744875487648774878487948804881488248834884488548864887488848894890489148924893489448954896489748984899490049014902490349044905490649074908490949104911491249134914491549164917491849194920492149224923492449254926492749284929493049314932493349344935493649374938493949404941494249434944494549464947494849494950495149524953495449554956495749584959496049614962496349644965496649674968496949704971497249734974497549764977497849794980498149824983498449854986498749884989499049914992499349944995499649974998499950005001500250035004500550065007500850095010501150125013501450155016501750185019502050215022502350245025502650275028502950305031503250335034503550365037503850395040504150425043504450455046504750485049505050515052505350545055505650575058505950605061506250635064506550665067506850695070507150725073507450755076507750785079508050815082508350845085508650875088508950905091509250935094509550965097509850995100510151025103510451055106510751085109511051115112511351145115511651175118511951205121512251235124512551265127512851295130513151325133513451355136513751385139514051415142514351445145514651475148514951505151515251535154515551565157515851595160516151625163516451655166516751685169517051715172517351745175517651775178517951805181518251835184518551865187518851895190519151925193519451955196519751985199520052015202520352045205520652075208520952105211521252135214521552165217521852195220522152225223522452255226522752285229523052315232523352345235523652375238523952405241524252435244524552465247524852495250525152525253525452555256525752585259526052615262526352645265526652675268526952705271527252735274527552765277527852795280528152825283528452855286528752885289529052915292529352945295529652975298529953005301530253035304530553065307530853095310531153125313531453155316531753185319532053215322532353245325532653275328532953305331533253335334533553365337533853395340534153425343534453455346534753485349535053515352535353545355535653575358535953605361536253635364536553665367536853695370537153725373537453755376537753785379538053815382538353845385538653875388538953905391539253935394539553965397539853995400540154025403540454055406540754085409541054115412541354145415541654175418541954205421542254235424542554265427542854295430543154325433543454355436543754385439544054415442544354445445544654475448544954505451545254535454545554565457545854595460546154625463546454655466546754685469547054715472547354745475547654775478547954805481548254835484548554865487548854895490549154925493549454955496549754985499550055015502550355045505550655075508550955105511551255135514551555165517551855195520552155225523552455255526552755285529553055315532553355345535553655375538553955405541554255435544554555465547554855495550555155525553555455555556555755585559556055615562556355645565556655675568556955705571557255735574557555765577557855795580558155825583558455855586558755885589559055915592559355945595559655975598559956005601560256035604560556065607560856095610561156125613561456155616561756185619562056215622562356245625562656275628562956305631563256335634563556365637563856395640564156425643564456455646564756485649565056515652565356545655565656575658565956605661566256635664566556665667566856695670567156725673567456755676567756785679568056815682568356845685568656875688568956905691569256935694569556965697569856995700570157025703570457055706570757085709571057115712571357145715571657175718571957205721572257235724572557265727572857295730573157325733573457355736573757385739574057415742574357445745574657475748574957505751575257535754575557565757575857595760576157625763576457655766576757685769577057715772577357745775577657775778577957805781578257835784578557865787578857895790579157925793579457955796579757985799580058015802580358045805580658075808580958105811581258135814581558165817581858195820582158225823582458255826582758285829583058315832583358345835583658375838583958405841584258435844584558465847584858495850585158525853585458555856585758585859586058615862586358645865586658675868586958705871587258735874587558765877587858795880588158825883588458855886588758885889589058915892589358945895589658975898589959005901590259035904590559065907590859095910591159125913591459155916591759185919592059215922592359245925592659275928592959305931593259335934593559365937593859395940594159425943594459455946594759485949595059515952595359545955595659575958595959605961596259635964596559665967596859695970597159725973597459755976597759785979598059815982598359845985598659875988598959905991599259935994599559965997599859996000600160026003600460056006600760086009601060116012601360146015601660176018601960206021602260236024602560266027602860296030603160326033603460356036603760386039604060416042604360446045604660476048604960506051605260536054605560566057605860596060606160626063606460656066606760686069607060716072607360746075607660776078607960806081608260836084608560866087608860896090609160926093609460956096609760986099610061016102610361046105610661076108610961106111611261136114611561166117611861196120612161226123612461256126612761286129613061316132613361346135613661376138613961406141614261436144614561466147614861496150615161526153615461556156615761586159616061616162616361646165616661676168616961706171617261736174617561766177617861796180618161826183618461856186618761886189619061916192619361946195619661976198619962006201620262036204620562066207620862096210621162126213621462156216621762186219622062216222622362246225622662276228622962306231623262336234623562366237623862396240624162426243624462456246624762486249625062516252625362546255625662576258625962606261626262636264626562666267626862696270627162726273627462756276627762786279628062816282628362846285628662876288628962906291629262936294629562966297629862996300630163026303630463056306630763086309631063116312631363146315631663176318631963206321632263236324632563266327632863296330633163326333633463356336633763386339634063416342634363446345634663476348634963506351635263536354635563566357635863596360636163626363636463656366636763686369637063716372637363746375637663776378637963806381638263836384638563866387638863896390639163926393639463956396639763986399640064016402640364046405640664076408640964106411641264136414641564166417641864196420642164226423642464256426642764286429643064316432643364346435643664376438643964406441644264436444644564466447644864496450645164526453645464556456645764586459646064616462646364646465646664676468646964706471647264736474647564766477647864796480648164826483648464856486648764886489649064916492649364946495649664976498649965006501650265036504650565066507650865096510651165126513651465156516651765186519652065216522652365246525652665276528652965306531653265336534653565366537653865396540654165426543654465456546654765486549655065516552655365546555655665576558655965606561656265636564656565666567656865696570657165726573657465756576657765786579658065816582658365846585658665876588658965906591659265936594659565966597659865996600660166026603660466056606660766086609 |
- //////// Start of inlined file: tigr_amalgamated.c ////////
- #include "tigr.h"
- //////// Start of inlined file: tigr_internal.h ////////
- // can't use pragma once here because this file probably will endup in .c
- #ifndef __TIGR_INTERNAL_H__
- #define __TIGR_INTERNAL_H__
- #define _CRT_SECURE_NO_WARNINGS NOPE
- // Graphics configuration.
- #ifndef TIGR_HEADLESS
- #define TIGR_GAPI_GL
- // Calculates the biggest scale that a bitmap can fit into an area at.
- int tigrCalcScale(int bmpW, int bmpH, int areaW, int areaH);
- #endif
- // Creates a new bitmap, with extra payload bytes.
- Tigr* tigrBitmap2(int w, int h, int extra);
- // Resizes an existing bitmap.
- void tigrResize(Tigr* bmp, int w, int h);
- // Calculates a new scale, taking minimum-scale flags into account.
- int tigrEnforceScale(int scale, int flags);
- // Calculates the correct position for a bitmap to fit into a window.
- void tigrPosition(Tigr* bmp, int scale, int windowW, int windowH, int out[4]);
- // ----------------------------------------------------------
- #ifdef _WIN32
- #define WIN32_LEAN_AND_MEAN
- #include <windows.h>
- #endif
- #if !defined(TIGR_HEADLESS) && __linux__ && !__ANDROID__
- #include <X11/X.h>
- #include <X11/Xlib.h>
- #endif
- #ifdef __APPLE__
- #include "TargetConditionals.h"
- #if TARGET_OS_IPHONE
- #define __IOS__ 1
- #else
- #define __MACOS__ 1
- #endif
- #endif
- #ifdef TIGR_GAPI_GL
- #if __MACOS__
- #define GL_SILENCE_DEPRECATION
- #include <OpenGL/gl3.h>
- #endif
- #ifdef _WIN32
- #include <GL/gl.h>
- #endif
- #if __linux__ && !__ANDROID__
- #define GL_GLEXT_PROTOTYPES
- #include <GL/gl.h>
- #include <GL/glx.h>
- #endif
- #if __ANDROID__
- #include <EGL/egl.h>
- #include <GLES3/gl3.h>
- #endif
- #if __IOS__
- #define GLES_SILENCE_DEPRECATION
- #include <OpenGLES/ES3/gl.h>
- #endif
- typedef struct {
- #ifdef _WIN32
- HGLRC hglrc;
- HDC dc;
- #endif
- #ifdef __APPLE__
- void* glContext;
- #endif
- GLuint tex[2];
- GLuint vao;
- GLuint program;
- GLuint uniform_projection;
- GLuint uniform_model;
- GLuint uniform_parameters;
- int gl_legacy;
- int gl_user_opengl_rendering;
- } GLStuff;
- #endif
- #define MAX_TOUCH_POINTS 10
- typedef struct {
- int shown, closed;
- #ifdef TIGR_GAPI_GL
- GLStuff gl;
- #ifdef _WIN32
- wchar_t* wtitle;
- DWORD dwStyle;
- RECT oldPos;
- #endif
- #ifdef __linux__
- #if __ANDROID__
- EGLContext context;
- #else
- Display* dpy;
- Window win;
- GLXContext glc;
- XIC ic;
- #endif // __ANDROID__
- #endif // __linux__
- #endif // TIGR_GAPI_GL
- Tigr* widgets;
- int widgetsWanted;
- unsigned char widgetAlpha;
- float widgetsScale;
- float p1, p2, p3, p4;
- int flags;
- int scale;
- int pos[4];
- int lastChar;
- char keys[256], prev[256];
- #if defined(__ANDROID__)
- char released[256];
- #endif // __ANDROID__
- #if defined(__MACOS__)
- int mouseInView;
- int mouseButtons;
- double frameTime;
- #endif // __MACOS__
- #if defined(__linux__) || defined(__IOS__)
- int mouseButtons;
- int mouseX;
- int mouseY;
- #endif // __linux__ __IOS__
- #if defined(__ANDROID__) || defined(__IOS__)
- int numTouchPoints;
- TigrTouchPoint touchPoints[MAX_TOUCH_POINTS];
- #endif // __ANDROID__ __IOS__
- #if defined(_WIN32) || defined(__linux__) || defined(__MACOS__)
- float scrollDeltaX;
- float scrollDeltaY;
- #endif // _WIN32 __linux__ __MACOS__
- } TigrInternal;
- // ----------------------------------------------------------
- TigrInternal* tigrInternal(Tigr* bmp);
- void tigrGAPICreate(Tigr* bmp);
- void tigrGAPIDestroy(Tigr* bmp);
- int tigrGAPIBegin(Tigr* bmp);
- int tigrGAPIEnd(Tigr* bmp);
- void tigrGAPIPresent(Tigr* bmp, int w, int h);
- #endif
- //////// End of inlined file: tigr_internal.h ////////
- //////// Start of inlined file: tigr_upscale_gl_vs.h ////////
- #ifndef __TIGR_UPSCALE_GL_VS_H__
- #define __TIGR_UPSCALE_GL_VS_H__
- //////// Start of inlined file: tigr_glsl_hdr.h ////////
- #ifndef __TIGR_GLSL_HDR_H__
- #define __TIGR_GLSL_HDR_H__
- #if __ANDROID__ || __IOS__
- #define GLSL_VERSION_HEADER \
- "#version 300 es\n" \
- "precision mediump float;\n"
- #else
- #define GLSL_VERSION_HEADER "#version 330 core\n"
- #endif
- #endif // __TIGR_GLSL_HDR_H__
- //////// End of inlined file: tigr_glsl_hdr.h ////////
- // clang-format off
- const char tigr_upscale_gl_vs[] = {
- GLSL_VERSION_HEADER
- "layout (location = 0) in vec2 pos_in;"
- "layout (location = 1) in vec2 uv_in;"
- "out vec2 uv;"
- "uniform mat4 model;"
- "uniform mat4 projection;"
- "void main()"
- "{"
- " uv = uv_in;"
- " gl_Position = projection * model * vec4(pos_in, 0.0, 1.0);"
- "}"
- };
- // clang-format on
- const int tigr_upscale_gl_vs_size = (int)sizeof(tigr_upscale_gl_vs) - 1;
- #endif
- //////// End of inlined file: tigr_upscale_gl_vs.h ////////
- //////// Start of inlined file: tigr_upscale_gl_fs.h ////////
- #ifndef __TIGR_UPSCALE_GL_FS_H__
- #define __TIGR_UPSCALE_GL_FS_H__
- //#include "tigr_glsl_hdr.h"
- // clang-format off
- const char tigr_upscale_gl_fs[] = {
- GLSL_VERSION_HEADER
- "in vec2 uv;"
- "out vec4 color;"
- "uniform sampler2D image;"
- "uniform vec4 parameters;"
- "void fxShader(out vec4 color, in vec2 coord);"
- "void main()"
- "{"
- " fxShader(color, uv);"
- "}\n"
- };
- // clang-format on
- const int tigr_upscale_gl_fs_size = (int)sizeof(tigr_upscale_gl_fs) - 1;
- // clang-format off
- const char tigr_default_fx_gl_fs[] = {
- "void fxShader(out vec4 color, in vec2 uv) {"
- " vec2 tex_size = vec2(textureSize(image, 0));"
- " vec2 uv_blur = mix(floor(uv * tex_size) + 0.5, uv * tex_size, parameters.xy) / tex_size;"
- " vec4 c = texture(image, uv_blur);"
- " c.rgb *= mix(0.5, 1.0 - fract(uv.y * tex_size.y), parameters.z) * 2.0; //scanline\n"
- " c = mix(vec4(0.5), c, parameters.w); //contrast\n"
- " color = c;"
- "}"
- };
- // clang-format on
- const int tigr_default_fx_gl_fs_size = (int)sizeof(tigr_default_fx_gl_fs) - 1;
- #endif
- //////// End of inlined file: tigr_upscale_gl_fs.h ////////
- //////// Start of inlined file: tigr_bitmaps.c ////////
- //#include "tigr_internal.h"
- #include <stdlib.h>
- #include <string.h>
- // Expands 0-255 into 0-256
- #define EXPAND(X) ((X) + ((X) > 0))
- #define CLIP0(CX, X, X2, W) \
- if (X < CX) { \
- int D = CX - X; \
- W -= D; \
- X2 += D; \
- X += D; \
- }
- #define CLIP1(X, DW, W) \
- if (X + W > DW) \
- W = DW - X;
- #define CLIP() \
- CLIP0(dst->cx, dx, sx, w); \
- CLIP0(dst->cy, dy, sy, h); \
- CLIP0(0, sx, dx, w); \
- CLIP0(0, sy, dy, h); \
- CLIP1(dx, dst->cx + cw, w); \
- CLIP1(dy, dst->cy + ch, h); \
- CLIP1(sx, src->w, w); \
- CLIP1(sy, src->h, h); \
- if (w <= 0 || h <= 0) \
- return
- Tigr* tigrBitmap2(int w, int h, int extra) {
- Tigr* tigr = (Tigr*)calloc(1, sizeof(Tigr) + extra);
- tigr->w = w;
- tigr->h = h;
- tigr->cw = -1;
- tigr->ch = -1;
- tigr->pix = (TPixel*)calloc(w * h, sizeof(TPixel));
- tigr->blitMode = TIGR_BLEND_ALPHA;
- return tigr;
- }
- Tigr* tigrBitmap(int w, int h) {
- return tigrBitmap2(w, h, 0);
- }
- #ifdef TIGR_HEADLESS
- void tigrFree(Tigr* bmp) {
- free(bmp->pix);
- free(bmp);
- }
- #endif // TIGR_HEADLESS
- void tigrResize(Tigr* bmp, int w, int h) {
- if (bmp->w == w && bmp->h == h) {
- return;
- }
- int y, cw, ch;
- TPixel* newpix = (TPixel*)calloc(w * h, sizeof(TPixel));
- cw = (w < bmp->w) ? w : bmp->w;
- ch = (h < bmp->h) ? h : bmp->h;
- // Copy any old data across.
- for (y = 0; y < ch; y++)
- memcpy(newpix + y * w, bmp->pix + y * bmp->w, cw * sizeof(TPixel));
- free(bmp->pix);
- bmp->pix = newpix;
- bmp->w = w;
- bmp->h = h;
- }
- #ifndef TIGR_HEADLESS
- int tigrCalcScale(int bmpW, int bmpH, int areaW, int areaH) {
- if (bmpW == 0 || bmpH == 0) {
- tigrError(0, "Invalid bitmap size, (%dx%d)", bmpW, bmpH);
- }
- // We want it as big as possible in the window, but still
- // maintaining the correct aspect ratio, and always
- // having an integer pixel size.
- int scale = 0;
- for (;;) {
- scale++;
- if (bmpW * scale > areaW || bmpH * scale > areaH) {
- scale--;
- break;
- }
- }
- return (scale > 1) ? scale : 1;
- }
- #endif // TIGR_HEADLESS
- int tigrEnforceScale(int scale, int flags) {
- if ((flags & TIGR_4X) && scale < 4)
- scale = 4;
- if ((flags & TIGR_3X) && scale < 3)
- scale = 3;
- if ((flags & TIGR_2X) && scale < 2)
- scale = 2;
- return scale;
- }
- void tigrPosition(Tigr* bmp, int scale, int windowW, int windowH, int out[4]) {
- // Center the image on screen at this scale.
- out[0] = (windowW - bmp->w * scale) / 2;
- out[1] = (windowH - bmp->h * scale) / 2;
- out[2] = out[0] + bmp->w * scale;
- out[3] = out[1] + bmp->h * scale;
- }
- void tigrClear(Tigr* bmp, TPixel color) {
- int count = bmp->w * bmp->h;
- int n;
- for (n = 0; n < count; n++)
- bmp->pix[n] = color;
- }
- void tigrFill(Tigr* bmp, int x, int y, int w, int h, TPixel color) {
- TPixel* td;
- int dt, i;
- if (x < 0) {
- w += x;
- x = 0;
- }
- if (y < 0) {
- h += y;
- y = 0;
- }
- if (x + w > bmp->w) {
- w = bmp->w - x;
- }
- if (y + h > bmp->h) {
- h = bmp->h - y;
- }
- if (w <= 0 || h <= 0)
- return;
- td = &bmp->pix[y * bmp->w + x];
- dt = bmp->w;
- do {
- for (i = 0; i < w; i++)
- td[i] = color;
- td += dt;
- } while (--h);
- }
- void tigrLine(Tigr* bmp, int x0, int y0, int x1, int y1, TPixel color) {
- int sx, sy, dx, dy, err, e2;
- dx = abs(x1 - x0);
- dy = abs(y1 - y0);
- if (x0 < x1)
- sx = 1;
- else
- sx = -1;
- if (y0 < y1)
- sy = 1;
- else
- sy = -1;
- err = dx - dy;
- do {
- tigrPlot(bmp, x0, y0, color);
- e2 = 2 * err;
- if (e2 > -dy) {
- err -= dy;
- x0 += sx;
- }
- if (e2 < dx) {
- err += dx;
- y0 += sy;
- }
- } while (x0 != x1 || y0 != y1);
- }
- void tigrFillRect(Tigr* bmp, int x, int y, int w, int h, TPixel color) {
- x += 1;
- y += 1;
- w -= 2;
- h -= 2;
- int cx = bmp->cx;
- int cy = bmp->cy;
- int cw = bmp->cw >= 0 ? bmp->cw : bmp->w;
- int ch = bmp->ch >= 0 ? bmp->ch : bmp->h;
- if (x < cx) {
- w += (x - cx);
- x = cx;
- }
- if (y < cy) {
- h += (y - cy);
- y = cy;
- }
- if (x + w > cx + cw) {
- w -= (x + w) - (cx + cw);
- }
- if (y + h > cy + ch) {
- h -= (y + h) - (cy + ch);
- }
- if (w <= 0 || h <= 0)
- return;
- TPixel* td = &bmp->pix[y * bmp->w + x];
- int dt = bmp->w;
- int xa = EXPAND(color.a);
- int a = xa * xa;
- do {
- for (int i = 0; i < w; i++) {
- td[i].r += (unsigned char)((color.r - td[i].r) * a >> 16);
- td[i].g += (unsigned char)((color.g - td[i].g) * a >> 16);
- td[i].b += (unsigned char)((color.b - td[i].b) * a >> 16);
- td[i].a += (bmp->blitMode) * (unsigned char)((color.a - td[i].a) * a >> 16);
- }
- td += dt;
- } while (--h);
- }
- void tigrRect(Tigr* bmp, int x, int y, int w, int h, TPixel color) {
- int x1, y1;
- if (w <= 0 || h <= 0) {
- return;
- }
- if (w == 1) {
- tigrLine(bmp, x, y, x, y + h, color);
- } else if (h == 1) {
- tigrLine(bmp, x, y, x + w, y, color);
- } else {
- x1 = x + w - 1;
- y1 = y + h - 1;
- tigrLine(bmp, x, y, x1, y, color);
- tigrLine(bmp, x1, y, x1, y1, color);
- tigrLine(bmp, x1, y1, x, y1, color);
- tigrLine(bmp, x, y1, x, y, color);
- }
- }
- void tigrFillCircle(Tigr* bmp, int x0, int y0, int r, TPixel color) {
- if (r <= 0) {
- return;
- }
- int E = 1 - r;
- int dx = 0;
- int dy = -2 * r;
- int x = 0;
- int y = r;
- tigrLine(bmp, x0 - r + 1, y0, x0 + r, y0, color);
- while (x < y - 1) {
- x++;
- if (E >= 0) {
- y--;
- dy += 2;
- E += dy;
- tigrLine(bmp, x0 - x + 1, y0 + y, x0 + x, y0 + y, color);
- tigrLine(bmp, x0 - x + 1, y0 - y, x0 + x, y0 - y, color);
- }
- dx += 2;
- E += dx + 1;
- if (x != y) {
- tigrLine(bmp, x0 - y + 1, y0 + x, x0 + y, y0 + x, color);
- tigrLine(bmp, x0 - y + 1, y0 - x, x0 + y, y0 - x, color);
- }
- }
- }
- void tigrCircle(Tigr* bmp, int x0, int y0, int r, TPixel color) {
- int E = 1 - r;
- int dx = 0;
- int dy = -2 * r;
- int x = 0;
- int y = r;
- tigrPlot(bmp, x0, y0 + r, color);
- tigrPlot(bmp, x0, y0 - r, color);
- tigrPlot(bmp, x0 + r, y0, color);
- tigrPlot(bmp, x0 - r, y0, color);
- while (x < y - 1) {
- x++;
- if (E >= 0) {
- y--;
- dy += 2;
- E += dy;
- }
- dx += 2;
- E += dx + 1;
- tigrPlot(bmp, x0 + x, y0 + y, color);
- tigrPlot(bmp, x0 - x, y0 + y, color);
- tigrPlot(bmp, x0 + x, y0 - y, color);
- tigrPlot(bmp, x0 - x, y0 - y, color);
- if (x != y) {
- tigrPlot(bmp, x0 + y, y0 + x, color);
- tigrPlot(bmp, x0 - y, y0 + x, color);
- tigrPlot(bmp, x0 + y, y0 - x, color);
- tigrPlot(bmp, x0 - y, y0 - x, color);
- }
- }
- }
- TPixel tigrGet(Tigr* bmp, int x, int y) {
- TPixel empty = { 0, 0, 0, 0 };
- if (x >= 0 && y >= 0 && x < bmp->w && y < bmp->h)
- return bmp->pix[y * bmp->w + x];
- return empty;
- }
- void tigrPlot(Tigr* bmp, int x, int y, TPixel pix) {
- int xa, i, a;
- int cx = bmp->cx;
- int cy = bmp->cy;
- int cw = bmp->cw >= 0 ? bmp->cw : bmp->w;
- int ch = bmp->ch >= 0 ? bmp->ch : bmp->h;
- if (x >= cx && y >= cy && x < cx + cw && y < cy + ch) {
- xa = EXPAND(pix.a);
- a = xa * xa;
- i = y * bmp->w + x;
- bmp->pix[i].r += (unsigned char)((pix.r - bmp->pix[i].r) * a >> 16);
- bmp->pix[i].g += (unsigned char)((pix.g - bmp->pix[i].g) * a >> 16);
- bmp->pix[i].b += (unsigned char)((pix.b - bmp->pix[i].b) * a >> 16);
- bmp->pix[i].a += (bmp->blitMode) * (unsigned char)((pix.a - bmp->pix[i].a) * a >> 16);
- }
- }
- void tigrClip(Tigr* bmp, int cx, int cy, int cw, int ch) {
- bmp->cx = cx;
- bmp->cy = cy;
- bmp->cw = cw;
- bmp->ch = ch;
- }
- void tigrBlit(Tigr* dst, Tigr* src, int dx, int dy, int sx, int sy, int w, int h) {
- int cw = dst->cw >= 0 ? dst->cw : dst->w;
- int ch = dst->ch >= 0 ? dst->ch : dst->h;
- CLIP();
- TPixel* ts = &src->pix[sy * src->w + sx];
- TPixel* td = &dst->pix[dy * dst->w + dx];
- int st = src->w;
- int dt = dst->w;
- do {
- memcpy(td, ts, w * sizeof(TPixel));
- ts += st;
- td += dt;
- } while (--h);
- }
- void tigrBlitTint(Tigr* dst, Tigr* src, int dx, int dy, int sx, int sy, int w, int h, TPixel tint) {
- int cw = dst->cw >= 0 ? dst->cw : dst->w;
- int ch = dst->ch >= 0 ? dst->ch : dst->h;
- CLIP();
- int xr = EXPAND(tint.r);
- int xg = EXPAND(tint.g);
- int xb = EXPAND(tint.b);
- int xa = EXPAND(tint.a);
- TPixel* ts = &src->pix[sy * src->w + sx];
- TPixel* td = &dst->pix[dy * dst->w + dx];
- int st = src->w;
- int dt = dst->w;
- do {
- for (int x = 0; x < w; x++) {
- unsigned r = (xr * ts[x].r) >> 8;
- unsigned g = (xg * ts[x].g) >> 8;
- unsigned b = (xb * ts[x].b) >> 8;
- unsigned a = xa * EXPAND(ts[x].a);
- td[x].r += (unsigned char)((r - td[x].r) * a >> 16);
- td[x].g += (unsigned char)((g - td[x].g) * a >> 16);
- td[x].b += (unsigned char)((b - td[x].b) * a >> 16);
- td[x].a += (dst->blitMode) * (unsigned char)((ts[x].a - td[x].a) * a >> 16);
- }
- ts += st;
- td += dt;
- } while (--h);
- }
- void tigrBlitAlpha(Tigr* dst, Tigr* src, int dx, int dy, int sx, int sy, int w, int h, float alpha) {
- alpha = (alpha < 0) ? 0 : (alpha > 1 ? 1 : alpha);
- tigrBlitTint(dst, src, dx, dy, sx, sy, w, h, tigrRGBA(0xff, 0xff, 0xff, (unsigned char)(alpha * 255)));
- }
- void tigrBlitMode(Tigr* dst, int mode) {
- dst->blitMode = mode;
- }
- #undef CLIP0
- #undef CLIP1
- #undef CLIP
- //////// End of inlined file: tigr_bitmaps.c ////////
- //////// Start of inlined file: tigr_loadpng.c ////////
- //#include "tigr_internal.h"
- #include <stdlib.h>
- #include <string.h>
- #include <errno.h>
- typedef struct {
- const unsigned char *p, *end;
- } PNG;
- static unsigned get32(const unsigned char* v) {
- return (v[0] << 24) | (v[1] << 16) | (v[2] << 8) | v[3];
- }
- static const unsigned char* find(PNG* png, const char* chunk, unsigned minlen) {
- const unsigned char* start;
- while (png->p < png->end) {
- unsigned len = get32(png->p + 0);
- start = png->p;
- png->p += len + 12;
- if (memcmp(start + 4, chunk, 4) == 0 && len >= minlen && png->p <= png->end)
- return start + 8;
- }
- return NULL;
- }
- static unsigned char paeth(unsigned char a, unsigned char b, unsigned char c) {
- int p = a + b - c;
- int pa = abs(p - a), pb = abs(p - b), pc = abs(p - c);
- return (pa <= pb && pa <= pc) ? a : (pb <= pc) ? b : c;
- }
- static int rowBytes(int w, int bipp) {
- int rowBits = w * bipp;
- return rowBits / 8 + ((rowBits % 8) ? 1 : 0);
- }
- static int unfilter(int w, int h, int bipp, unsigned char* raw) {
- int len = rowBytes(w, bipp);
- int bpp = rowBytes(1, bipp);
- int x, y;
- unsigned char* first = (unsigned char*)malloc(len + 1);
- memset(first, 0, len + 1);
- unsigned char* prev = first;
- for (y = 0; y < h; y++, prev = raw, raw += len) {
- #define LOOP(A, B) \
- for (x = 0; x < bpp; x++) \
- raw[x] += A; \
- for (; x < len; x++) \
- raw[x] += B; \
- break
- switch (*raw++) {
- case 0:
- break;
- case 1:
- LOOP(0, raw[x - bpp]);
- case 2:
- LOOP(prev[x], prev[x]);
- case 3:
- LOOP(prev[x] / 2, (raw[x - bpp] + prev[x]) / 2);
- case 4:
- LOOP(prev[x], paeth(raw[x - bpp], prev[x], prev[x - bpp]));
- default:
- return 0;
- }
- #undef LOOP
- }
- free(first);
- return 1;
- }
- static void convert(int bypp, int w, int h, const unsigned char* src, TPixel* dest, const unsigned char* trns) {
- int x, y;
- for (y = 0; y < h; y++) {
- src++; // skip filter byte
- for (x = 0; x < w; x++, src += bypp) {
- switch (bypp) {
- case 1: {
- unsigned char c = src[0];
- if (trns && c == *trns) {
- *dest++ = tigrRGBA(c, c, c, 0);
- break;
- } else {
- *dest++ = tigrRGB(c, c, c);
- break;
- }
- }
- case 2:
- *dest++ = tigrRGBA(src[0], src[0], src[0], src[1]);
- break;
- case 3: {
- unsigned char r = src[0];
- unsigned char g = src[1];
- unsigned char b = src[2];
- if (trns && trns[1] == r && trns[3] == g && trns[5] == b) {
- *dest++ = tigrRGBA(r, g, b, 0);
- break;
- } else {
- *dest++ = tigrRGB(r, g, b);
- break;
- }
- }
- case 4:
- *dest++ = tigrRGBA(src[0], src[1], src[2], src[3]);
- break;
- }
- }
- }
- }
- static void depalette(int w,
- int h,
- unsigned char* src,
- TPixel* dest,
- int bipp,
- const unsigned char* plte,
- const unsigned char* trns,
- int trnsSize) {
- int x, y, c;
- unsigned char alpha;
- int mask = 0, len = 0;
- switch (bipp) {
- case 4:
- mask = 15;
- len = 1;
- break;
- case 2:
- mask = 3;
- len = 3;
- break;
- case 1:
- mask = 1;
- len = 7;
- }
- for (y = 0; y < h; y++) {
- src++; // skip filter byte
- for (x = 0; x < w; x++) {
- if (bipp == 8) {
- c = *src++;
- } else {
- int pos = x & len;
- c = (src[0] >> ((len - pos) * bipp)) & mask;
- if (pos == len) {
- src++;
- }
- }
- alpha = 255;
- if (c < trnsSize) {
- alpha = trns[c];
- }
- *dest++ = tigrRGBA(plte[c * 3 + 0], plte[c * 3 + 1], plte[c * 3 + 2], alpha);
- }
- }
- }
- #define FAIL() \
- { \
- errno = EINVAL; \
- goto err; \
- }
- #define CHECK(X) \
- if (!(X)) \
- FAIL()
- static int outsize(Tigr* bmp, int bipp) {
- return (rowBytes(bmp->w, bipp) + 1) * bmp->h;
- }
- static Tigr* tigrLoadPng(PNG* png) {
- const unsigned char *ihdr, *idat, *plte, *trns, *first;
- int trnsSize = 0;
- int depth, ctype, bipp;
- int datalen = 0;
- unsigned char *data = NULL, *out;
- Tigr* bmp = NULL;
- CHECK(memcmp(png->p, "\211PNG\r\n\032\n", 8) == 0); // PNG signature
- png->p += 8;
- first = png->p;
- // Read IHDR
- ihdr = find(png, "IHDR", 13);
- CHECK(ihdr);
- depth = ihdr[8];
- ctype = ihdr[9];
- switch (ctype) {
- case 0:
- bipp = depth;
- break; // greyscale
- case 2:
- bipp = 3 * depth;
- break; // RGB
- case 3:
- bipp = depth;
- break; // paletted
- case 4:
- bipp = 2 * depth;
- break; // grey+alpha
- case 6:
- bipp = 4 * depth;
- break; // RGBA
- default:
- FAIL();
- }
- // Allocate bitmap (+1 width to save room for stupid PNG filter bytes)
- bmp = tigrBitmap(get32(ihdr + 0) + 1, get32(ihdr + 4));
- CHECK(bmp);
- bmp->w--;
- // We support 8-bit color components and 1, 2, 4 and 8 bit palette formats.
- // No interlacing, or wacky filter types.
- CHECK((depth != 16) && ihdr[10] == 0 && ihdr[11] == 0 && ihdr[12] == 0);
- // Join IDAT chunks.
- for (idat = find(png, "IDAT", 0); idat; idat = find(png, "IDAT", 0)) {
- unsigned len = get32(idat - 8);
- data = (unsigned char*)realloc(data, datalen + len);
- if (!data)
- break;
- memcpy(data + datalen, idat, len);
- datalen += len;
- }
- // Find palette.
- png->p = first;
- plte = find(png, "PLTE", 0);
- // Find transparency info.
- png->p = first;
- trns = find(png, "tRNS", 0);
- if (trns) {
- trnsSize = get32(trns - 8);
- }
- CHECK(data && datalen >= 6);
- CHECK((data[0] & 0x0f) == 0x08 // compression method (RFC 1950)
- && (data[0] & 0xf0) <= 0x70 // window size
- && (data[1] & 0x20) == 0); // preset dictionary present
- out = (unsigned char*)bmp->pix + outsize(bmp, 32) - outsize(bmp, bipp);
- CHECK(tigrInflate(out, outsize(bmp, bipp), data + 2, datalen - 6));
- CHECK(unfilter(bmp->w, bmp->h, bipp, out));
- if (ctype == 3) {
- CHECK(plte);
- depalette(bmp->w, bmp->h, out, bmp->pix, bipp, plte, trns, trnsSize);
- } else {
- CHECK(bipp % 8 == 0);
- convert(bipp / 8, bmp->w, bmp->h, out, bmp->pix, trns);
- }
- free(data);
- return bmp;
- err:
- if (data)
- free(data);
- if (bmp)
- tigrFree(bmp);
- return NULL;
- }
- #undef CHECK
- #undef FAIL
- Tigr* tigrLoadImageMem(const void* data, int length) {
- PNG png;
- png.p = (unsigned char*)data;
- png.end = (unsigned char*)data + length;
- return tigrLoadPng(&png);
- }
- Tigr* tigrLoadImage(const char* fileName) {
- int len;
- void* data;
- Tigr* bmp;
- data = tigrReadFile(fileName, &len);
- if (!data)
- return NULL;
- bmp = tigrLoadImageMem(data, len);
- free(data);
- return bmp;
- }
- //////// End of inlined file: tigr_loadpng.c ////////
- //////// Start of inlined file: tigr_savepng.c ////////
- //#include "tigr_internal.h"
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #include <errno.h>
- typedef struct {
- unsigned crc, adler, bits, prev, runlen;
- FILE* out;
- unsigned crcTable[256];
- } Save;
- static const unsigned crctable[16] = { 0, 0x1db71064, 0x3b6e20c8, 0x26d930ac, 0x76dc4190, 0x6b6b51f4,
- 0x4db26158, 0x5005713c, 0xedb88320, 0xf00f9344, 0xd6d6a3e8, 0xcb61b38c,
- 0x9b64c2b0, 0x86d3d2d4, 0xa00ae278, 0xbdbdf21c };
- static void put(Save* s, unsigned v) {
- fputc(v, s->out);
- s->crc = (s->crc >> 4) ^ crctable[(s->crc & 15) ^ (v & 15)];
- s->crc = (s->crc >> 4) ^ crctable[(s->crc & 15) ^ (v >> 4)];
- }
- static void updateAdler(Save* s, unsigned v) {
- unsigned s1 = s->adler & 0xffff, s2 = (s->adler >> 16) & 0xffff;
- s1 = (s1 + v) % 65521;
- s2 = (s2 + s1) % 65521;
- s->adler = (s2 << 16) + s1;
- }
- static void put32(Save* s, unsigned v) {
- put(s, (v >> 24) & 0xff);
- put(s, (v >> 16) & 0xff);
- put(s, (v >> 8) & 0xff);
- put(s, v & 0xff);
- }
- void putbits(Save* s, unsigned data, unsigned bitcount) {
- while (bitcount--) {
- unsigned prev = s->bits;
- s->bits = (s->bits >> 1) | ((data & 1) << 7);
- data >>= 1;
- if (prev & 1) {
- put(s, s->bits);
- s->bits = 0x80;
- }
- }
- }
- void putbitsr(Save* s, unsigned data, unsigned bitcount) {
- while (bitcount--)
- putbits(s, data >> bitcount, 1);
- }
- static void begin(Save* s, const char* id, unsigned len) {
- put32(s, len);
- s->crc = 0xffffffff;
- put(s, id[0]);
- put(s, id[1]);
- put(s, id[2]);
- put(s, id[3]);
- }
- static void literal(Save* s, unsigned v) {
- // Encode a literal/length using the built-in tables.
- // Could do better with a custom table but whatever.
- if (v < 144)
- putbitsr(s, 0x030 + v - 0, 8);
- else if (v < 256)
- putbitsr(s, 0x190 + v - 144, 9);
- else if (v < 280)
- putbitsr(s, 0x000 + v - 256, 7);
- else
- putbitsr(s, 0x0c0 + v - 280, 8);
- }
- static void encodelen(Save* s, unsigned code, unsigned bits, unsigned len) {
- literal(s, code + (len >> bits));
- putbits(s, len, bits);
- putbits(s, 0, 5);
- }
- static void endrun(Save* s) {
- s->runlen--;
- literal(s, s->prev);
- if (s->runlen >= 67)
- encodelen(s, 277, 4, s->runlen - 67);
- else if (s->runlen >= 35)
- encodelen(s, 273, 3, s->runlen - 35);
- else if (s->runlen >= 19)
- encodelen(s, 269, 2, s->runlen - 19);
- else if (s->runlen >= 11)
- encodelen(s, 265, 1, s->runlen - 11);
- else if (s->runlen >= 3)
- encodelen(s, 257, 0, s->runlen - 3);
- else
- while (s->runlen--)
- literal(s, s->prev);
- }
- static void encodeByte(Save* s, unsigned char v) {
- updateAdler(s, v);
- // Simple RLE compression. We could do better by doing a search
- // to find matches, but this works pretty well TBH.
- if (s->prev == v && s->runlen < 115) {
- s->runlen++;
- } else {
- if (s->runlen)
- endrun(s);
- s->prev = v;
- s->runlen = 1;
- }
- }
- static void savePngHeader(Save* s, Tigr* bmp) {
- fwrite("\211PNG\r\n\032\n", 8, 1, s->out);
- begin(s, "IHDR", 13);
- put32(s, bmp->w);
- put32(s, bmp->h);
- put(s, 8); // bit depth
- put(s, 6); // RGBA
- put(s, 0); // compression (deflate)
- put(s, 0); // filter (standard)
- put(s, 0); // interlace off
- put32(s, ~s->crc);
- }
- static long savePngData(Save* s, Tigr* bmp, long dataPos) {
- int x, y;
- long dataSize;
- begin(s, "IDAT", 0);
- put(s, 0x08); // zlib compression method
- put(s, 0x1d); // zlib compression flags
- putbits(s, 3, 3); // zlib last block + fixed dictionary
- for (y = 0; y < bmp->h; y++) {
- TPixel* row = &bmp->pix[y * bmp->w];
- TPixel prev = tigrRGBA(0, 0, 0, 0);
- encodeByte(s, 1); // sub filter
- for (x = 0; x < bmp->w; x++) {
- encodeByte(s, row[x].r - prev.r);
- encodeByte(s, row[x].g - prev.g);
- encodeByte(s, row[x].b - prev.b);
- encodeByte(s, row[x].a - prev.a);
- prev = row[x];
- }
- }
- endrun(s);
- literal(s, 256); // terminator
- while (s->bits != 0x80)
- putbits(s, 0, 1);
- put32(s, s->adler);
- dataSize = (ftell(s->out) - dataPos) - 8;
- put32(s, ~s->crc);
- return dataSize;
- }
- int tigrSaveImage(const char* fileName, Tigr* bmp) {
- Save s;
- long dataPos, dataSize, err;
- // TODO - unicode?
- FILE* out = fopen(fileName, "wb");
- if (!out)
- return 1;
- s.out = out;
- s.adler = 1;
- s.bits = 0x80;
- s.prev = 0xffff;
- s.runlen = 0;
- savePngHeader(&s, bmp);
- dataPos = ftell(s.out);
- dataSize = savePngData(&s, bmp, dataPos);
- // End chunk.
- begin(&s, "IEND", 0);
- put32(&s, ~s.crc);
- // Write back payload size.
- fseek(out, dataPos, SEEK_SET);
- put32(&s, dataSize);
- err = ferror(out);
- fclose(out);
- return !err;
- }
- //////// End of inlined file: tigr_savepng.c ////////
- //////// Start of inlined file: tigr_inflate.c ////////
- //#include "tigr_internal.h"
- #include <stdlib.h>
- #include <setjmp.h>
- typedef struct {
- unsigned bits, count;
- const unsigned char *in, *inend;
- unsigned char *out, *outend;
- jmp_buf jmp;
- unsigned litcodes[288], distcodes[32], lencodes[19];
- int tlit, tdist, tlen;
- } State;
- #define FAIL() longjmp(s->jmp, 1)
- #define CHECK(X) \
- if (!(X)) \
- FAIL()
- // Built-in DEFLATE standard tables.
- static char order[] = { 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 };
- static char lenBits[29 + 2] = { 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2,
- 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0, 0, 0 };
- static int lenBase[29 + 2] = { 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31,
- 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0 };
- static char distBits[30 + 2] = { 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6,
- 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13, 0, 0 };
- static int distBase[30 + 2] = {
- 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129,
- 193, 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, 8193, 12289, 16385, 24577
- };
- // Table to bit-reverse a byte.
- static const unsigned char reverseTable[256] = {
- #define R2(n) n, n + 128, n + 64, n + 192
- #define R4(n) R2(n), R2(n + 32), R2(n + 16), R2(n + 48)
- #define R6(n) R4(n), R4(n + 8), R4(n + 4), R4(n + 12)
- R6(0), R6(2), R6(1), R6(3)
- };
- static unsigned rev16(unsigned n) {
- return (reverseTable[n & 0xff] << 8) | reverseTable[(n >> 8) & 0xff];
- }
- static int bits(State* s, int n) {
- int v = s->bits & ((1 << n) - 1);
- s->bits >>= n;
- s->count -= n;
- while (s->count < 16) {
- CHECK(s->in != s->inend);
- s->bits |= (*s->in++) << s->count;
- s->count += 8;
- }
- return v;
- }
- static unsigned char* emit(State* s, int len) {
- s->out += len;
- CHECK(s->out <= s->outend);
- return s->out - len;
- }
- static void copy(State* s, const unsigned char* src, int len) {
- unsigned char* dest = emit(s, len);
- while (len--)
- *dest++ = *src++;
- }
- static int build(State* s, unsigned* tree, unsigned char* lens, unsigned int symcount) {
- unsigned int codes[16], first[16], counts[16] = { 0 };
- // Frequency count.
- for (unsigned int n = 0; n < symcount; n++)
- counts[lens[n]]++;
- // Distribute codes.
- counts[0] = codes[0] = first[0] = 0;
- for (unsigned int n = 1; n <= 15; n++) {
- codes[n] = (codes[n - 1] + counts[n - 1]) << 1;
- first[n] = first[n - 1] + counts[n - 1];
- }
- CHECK(first[15] + counts[15] <= symcount);
- // Insert keys into the tree for each symbol.
- for (unsigned int n = 0; n < symcount; n++) {
- int len = lens[n];
- if (len != 0) {
- unsigned code = codes[len]++, slot = first[len]++;
- tree[slot] = (code << (32 - len)) | (n << 4) | len;
- }
- }
- return first[15];
- }
- static int decode(State* s, unsigned tree[], int max) {
- // Find the next prefix code.
- unsigned lo = 0, hi = max, key;
- unsigned search = (rev16(s->bits) << 16) | 0xffff;
- while (lo < hi) {
- unsigned guess = (lo + hi) / 2;
- if (search < tree[guess])
- hi = guess;
- else
- lo = guess + 1;
- }
- // Pull out the key and check it.
- key = tree[lo - 1];
- CHECK(((search ^ key) >> (32 - (key & 0xf))) == 0);
- bits(s, key & 0xf);
- return (key >> 4) & 0xfff;
- }
- static void run(State* s, int sym) {
- int length = bits(s, lenBits[sym]) + lenBase[sym];
- int dsym = decode(s, s->distcodes, s->tdist);
- int offs = bits(s, distBits[dsym]) + distBase[dsym];
- copy(s, s->out - offs, length);
- }
- static void block(State* s) {
- for (;;) {
- int sym = decode(s, s->litcodes, s->tlit);
- if (sym < 256)
- *emit(s, 1) = (unsigned char)sym;
- else if (sym > 256)
- run(s, sym - 257);
- else
- break;
- }
- }
- static void stored(State* s) {
- // Uncompressed data block.
- int len;
- bits(s, s->count & 7);
- len = bits(s, 16);
- CHECK(((len ^ s->bits) & 0xffff) == 0xffff);
- CHECK(s->in + len <= s->inend);
- copy(s, s->in, len);
- s->in += len;
- bits(s, 16);
- }
- static void fixed(State* s) {
- // Fixed set of Huffman codes.
- int n;
- unsigned char lens[288 + 32];
- for (n = 0; n <= 143; n++)
- lens[n] = 8;
- for (n = 144; n <= 255; n++)
- lens[n] = 9;
- for (n = 256; n <= 279; n++)
- lens[n] = 7;
- for (n = 280; n <= 287; n++)
- lens[n] = 8;
- for (n = 0; n < 32; n++)
- lens[288 + n] = 5;
- // Build lit/dist trees.
- s->tlit = build(s, s->litcodes, lens, 288);
- s->tdist = build(s, s->distcodes, lens + 288, 32);
- }
- static void dynamic(State* s) {
- int n, i, nlit, ndist, nlen;
- unsigned char lenlens[19] = { 0 }, lens[288 + 32];
- nlit = 257 + bits(s, 5);
- ndist = 1 + bits(s, 5);
- nlen = 4 + bits(s, 4);
- for (n = 0; n < nlen; n++)
- lenlens[(int) order[n]] = (unsigned char)bits(s, 3);
- // Build the tree for decoding code lengths.
- s->tlen = build(s, s->lencodes, lenlens, 19);
- // Decode code lengths.
- for (n = 0; n < nlit + ndist;) {
- int sym = decode(s, s->lencodes, s->tlen);
- switch (sym) {
- case 16:
- for (i = 3 + bits(s, 2); i; i--, n++)
- lens[n] = lens[n - 1];
- break;
- case 17:
- for (i = 3 + bits(s, 3); i; i--, n++)
- lens[n] = 0;
- break;
- case 18:
- for (i = 11 + bits(s, 7); i; i--, n++)
- lens[n] = 0;
- break;
- default:
- lens[n++] = (unsigned char)sym;
- break;
- }
- }
- // Build lit/dist trees.
- s->tlit = build(s, s->litcodes, lens, nlit);
- s->tdist = build(s, s->distcodes, lens + nlit, ndist);
- }
- int tigrInflate(void* out, unsigned outlen, const void* in, unsigned inlen) {
- int last;
- State* s = (State*)calloc(1, sizeof(State));
- // We assume we can buffer 2 extra bytes from off the end of 'in'.
- s->in = (unsigned char*)in;
- s->inend = s->in + inlen + 2;
- s->out = (unsigned char*)out;
- s->outend = s->out + outlen;
- s->bits = 0;
- s->count = 0;
- bits(s, 0);
- if (setjmp(s->jmp) == 1) {
- free(s);
- return 0;
- }
- do {
- last = bits(s, 1);
- switch (bits(s, 2)) {
- case 0:
- stored(s);
- break;
- case 1:
- fixed(s);
- block(s);
- break;
- case 2:
- dynamic(s);
- block(s);
- break;
- case 3:
- FAIL();
- }
- } while (!last);
- free(s);
- return 1;
- }
- #undef CHECK
- #undef FAIL
- //////// End of inlined file: tigr_inflate.c ////////
- //////// Start of inlined file: tigr_print.c ////////
- //#include "tigr_internal.h"
- //////// Start of inlined file: tigr_font.h ////////
- // Auto-generated by incbin.pl from font.png
- const unsigned char tigr_font[] = {
- 0x89,0x50,0x4e,0x47,0x0d,0x0a,0x1a,0x0a,0x00,0x00,0x00,0x0d,0x49,0x48,0x44,0x52,
- 0x00,0x00,0x00,0xfd,0x00,0x00,0x00,0x5c,0x08,0x03,0x00,0x00,0x00,0x92,0xab,0x43,
- 0x85,0x00,0x00,0x03,0x00,0x50,0x4c,0x54,0x45,0x5f,0x53,0x87,0x00,0x00,0x00,0x54,
- 0x54,0x54,0xff,0xff,0xfa,0x31,0x2a,0x42,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,
- 0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,
- 0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,
- 0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,
- 0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,
- 0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,
- 0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,
- 0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,
- 0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,
- 0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,
- 0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,
- 0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,
- 0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,
- 0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,
- 0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,
- 0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,
- 0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,
- 0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,
- 0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,
- 0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,
- 0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,
- 0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,
- 0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,
- 0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,
- 0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,
- 0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,
- 0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,
- 0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,
- 0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,
- 0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,
- 0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,
- 0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,
- 0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,
- 0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,
- 0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,
- 0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,
- 0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,
- 0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,
- 0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,
- 0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,
- 0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,
- 0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,
- 0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,
- 0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,
- 0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,
- 0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,
- 0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,
- 0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x4a,0x44,0xb7,0x5a,0x00,0x00,0x00,
- 0x01,0x74,0x52,0x4e,0x53,0x00,0x40,0xe6,0xd8,0x66,0x00,0x00,0x00,0x01,0x62,0x4b,
- 0x47,0x44,0x00,0x88,0x05,0x1d,0x48,0x00,0x00,0x00,0x09,0x70,0x48,0x59,0x73,0x00,
- 0x00,0x0b,0x13,0x00,0x00,0x0b,0x13,0x01,0x00,0x9a,0x9c,0x18,0x00,0x00,0x00,0x07,
- 0x74,0x49,0x4d,0x45,0x07,0xe5,0x04,0x1c,0x16,0x0f,0x2b,0x56,0x9d,0x54,0x3b,0x00,
- 0x00,0x0b,0x7c,0x49,0x44,0x41,0x54,0x78,0xda,0xed,0x5c,0x89,0x9a,0xec,0xac,0x0a,
- 0x04,0xf5,0xfd,0x9f,0xf9,0x4e,0x94,0xa5,0x40,0x93,0x98,0xee,0x3e,0x73,0xe7,0xfb,
- 0x67,0xce,0xd2,0x66,0x31,0x6a,0x29,0x51,0x4a,0x20,0xd4,0x7e,0xf3,0x1f,0x6a,0x34,
- 0xfe,0xb4,0x5a,0xf9,0xf8,0x7f,0x24,0x54,0xf9,0xf8,0xf7,0x75,0xeb,0xeb,0x97,0xe0,
- 0xd4,0x73,0x92,0x64,0xd7,0xab,0x90,0xa4,0xc3,0x91,0x59,0xb2,0x56,0x29,0x50,0xce,
- 0xed,0xfa,0x71,0x74,0x94,0xd7,0x2f,0xd8,0xf5,0x7c,0x0e,0xf9,0xb1,0x74,0x69,0x15,
- 0x13,0x14,0xfd,0x09,0xf4,0xd2,0x08,0x3e,0xfe,0xf6,0x53,0x86,0x9c,0xa5,0x8d,0xcc,
- 0xd2,0x45,0x9a,0x2c,0xd0,0x7f,0x65,0x63,0x45,0xc1,0xa3,0x00,0xae,0x78,0x7d,0xb4,
- 0xb9,0x9f,0xf3,0x57,0xb9,0x7a,0xfd,0x38,0xc3,0x7c,0x90,0x36,0xad,0xba,0x68,0x75,
- 0x1c,0x8a,0x7e,0x05,0x3d,0x1f,0xcf,0xfa,0x18,0x0c,0xd8,0xad,0xff,0x96,0x9e,0xa3,
- 0xd7,0xc0,0x36,0xec,0x90,0xe9,0x0c,0x7d,0x1d,0x68,0x6c,0x6c,0x7b,0x01,0x5f,0xa5,
- 0x0d,0x74,0x23,0x25,0xe9,0xf5,0xd1,0xbb,0xbd,0x22,0x62,0xc1,0xa5,0xd7,0x53,0x2a,
- 0x02,0x38,0x9a,0x35,0x04,0xa4,0x8f,0x90,0x16,0xf9,0x0a,0x7a,0xfa,0x7a,0xb6,0x8f,
- 0xe9,0xd1,0x09,0x63,0x6c,0x8f,0x46,0x70,0xd1,0x7e,0x1d,0x08,0x07,0x20,0x41,0xdf,
- 0x81,0x15,0x6b,0x03,0x11,0x1e,0xf6,0x3e,0xb1,0xde,0xec,0xc3,0x22,0x12,0x31,0x46,
- 0x4d,0x9f,0xfd,0xaa,0x41,0x7b,0x5d,0xf3,0xb3,0xe7,0xe3,0x5a,0x3d,0x1d,0xbd,0xd6,
- 0xff,0x1f,0xe0,0xa9,0x4b,0x48,0x2f,0x6c,0x0c,0xc2,0x78,0xe4,0x25,0xf4,0x32,0x6a,
- 0x2a,0xf9,0xac,0xaf,0xeb,0xa8,0xa6,0x8e,0x54,0x87,0x27,0x8c,0xfd,0xd1,0x5f,0x63,
- 0xb8,0xbc,0x39,0xe4,0xa3,0x63,0x12,0x2e,0x6f,0x49,0xbf,0xfe,0xd5,0x60,0xc6,0xf7,
- 0x3d,0x4b,0xba,0xe6,0xb3,0x73,0xd6,0xeb,0xa3,0x11,0x2a,0xec,0x15,0x5f,0x83,0xf1,
- 0xc8,0x53,0xf4,0xec,0xaf,0x50,0x92,0xfc,0xa3,0xdc,0x31,0x24,0x7a,0x8e,0xe8,0x0f,
- 0xc1,0x18,0xd5,0xae,0x24,0x5f,0x85,0xa6,0x04,0xf4,0xde,0x8d,0x84,0xa8,0xb3,0xa4,
- 0x6b,0x3e,0x3b,0xf7,0xfb,0x0d,0x85,0x7d,0x74,0xb6,0x74,0xe8,0xa8,0xe2,0x8d,0xb1,
- 0x07,0xc9,0xaf,0xa3,0xdc,0xca,0x3a,0x24,0x32,0xa8,0x8e,0x9e,0x48,0x90,0x68,0x43,
- 0x1b,0x4e,0xa3,0xa9,0xf5,0x36,0xeb,0x15,0x5a,0xa3,0xca,0x69,0xc8,0x37,0xbd,0xf7,
- 0x2a,0xec,0x63,0xce,0x97,0x66,0xf4,0xf4,0x33,0x92,0xdf,0xfb,0x13,0x3b,0x43,0xc6,
- 0x9e,0x6d,0xe2,0x8f,0x2b,0xde,0x84,0x5e,0x32,0xfb,0xdc,0x80,0x92,0x2e,0xd7,0x4d,
- 0x06,0xf0,0xfd,0x96,0xc9,0x05,0x9f,0xb7,0x74,0xcc,0x4d,0xc7,0x73,0x30,0xe7,0x17,
- 0xa2,0xd7,0xc7,0x1e,0x24,0x7f,0x4c,0x7e,0x36,0x9e,0x76,0x7a,0x48,0x98,0xbe,0xd0,
- 0x32,0xf3,0xe4,0xf5,0x1e,0x06,0xa3,0xe7,0x28,0x92,0xb9,0x67,0x65,0x11,0x52,0x4d,
- 0xf5,0xfa,0x91,0x3d,0x9c,0x5b,0xfe,0x42,0xf8,0x3c,0xe4,0x6f,0xd6,0xf1,0xa1,0x01,
- 0x2f,0xa3,0xcf,0x05,0x9d,0x9d,0x7a,0xce,0x36,0x67,0x6a,0x51,0x1a,0xda,0x69,0x01,
- 0xef,0xa6,0xcd,0x3a,0x3e,0xa3,0x7f,0x4d,0xf2,0x3f,0x83,0x3e,0x4a,0xc3,0x3f,0x44,
- 0x9f,0x4b,0x4f,0x55,0xfd,0x7f,0xd0,0xaf,0x0e,0x7f,0x32,0xfa,0xdf,0xce,0x72,0x54,
- 0x01,0xa5,0xac,0x6a,0xa5,0xf3,0x3c,0x65,0xfb,0x7d,0x5d,0x88,0xe0,0x7a,0x5a,0xc0,
- 0x74,0xe1,0xca,0xeb,0xf9,0x65,0x85,0x34,0x55,0x3c,0xad,0x7c,0x34,0x1f,0x60,0x53,
- 0xaa,0x2f,0x3c,0x35,0x90,0x92,0x21,0x20,0x63,0x3d,0x63,0xd5,0x66,0xce,0x48,0x05,
- 0x67,0x12,0x82,0x64,0x44,0xab,0x64,0xd3,0x8c,0xd9,0x16,0xac,0x88,0xde,0xf5,0xfd,
- 0xdb,0x8a,0x30,0xd5,0x82,0xf7,0xd1,0x83,0xd6,0x58,0x7c,0xd9,0xe5,0x26,0x7a,0x9b,
- 0xa3,0xef,0x7a,0x98,0x6a,0x33,0x53,0xe1,0xbc,0x44,0x11,0xc9,0x48,0x56,0x66,0xc6,
- 0xea,0x4d,0xc2,0x18,0x0b,0xa2,0x8f,0xc2,0xb3,0xaa,0x30,0x8d,0xa0,0xe8,0xcc,0xa6,
- 0x25,0xad,0xd1,0x33,0x56,0x55,0x55,0xdd,0x57,0x32,0xc6,0xa6,0x60,0x1e,0x60,0x61,
- 0x4d,0x6e,0x83,0xbf,0xaa,0x2e,0x07,0x8d,0x0b,0xad,0x76,0x71,0x52,0xc5,0x9a,0x43,
- 0x2f,0x98,0xa6,0x9d,0xd0,0x9b,0x9a,0xae,0xe5,0xd8,0x98,0xdf,0xa0,0x77,0xed,0x86,
- 0x61,0x11,0x3f,0x45,0x4f,0x2c,0x55,0xb9,0xfe,0xe4,0x10,0x1a,0x39,0xfa,0x63,0x35,
- 0xf4,0x35,0x59,0xd0,0x93,0x6b,0xb2,0x31,0x4d,0xe8,0x59,0x15,0x57,0x4d,0xeb,0x44,
- 0x4a,0xa6,0xb1,0x0f,0xe8,0x95,0xcb,0xc5,0x57,0x06,0xbb,0x3b,0x14,0xc8,0x71,0x57,
- 0x64,0x81,0xde,0xe9,0x93,0xd0,0x4f,0xe1,0x4f,0xd0,0xf1,0x42,0x07,0x08,0x95,0x59,
- 0x40,0xdf,0xe5,0xe6,0xec,0xb5,0x4a,0x95,0x26,0xc9,0x0f,0x20,0x16,0xe8,0xf9,0x72,
- 0xd6,0x43,0xb4,0xda,0xd6,0x58,0x20,0xd7,0xc4,0x0f,0x16,0x73,0x9b,0x6f,0x10,0x60,
- 0x3f,0x39,0xc3,0x10,0x9a,0xbd,0x46,0xcf,0x75,0x28,0xb0,0xb4,0xf1,0x1a,0xce,0xef,
- 0xfd,0x0a,0x3d,0x8e,0xf9,0x2e,0x7a,0x7b,0x35,0x72,0xaf,0xac,0xd0,0x03,0x6f,0xb0,
- 0x07,0x0a,0x9f,0xf1,0x28,0x25,0x43,0x4b,0xf4,0xc4,0x75,0xb5,0x72,0x05,0xb2,0x91,
- 0x49,0x4a,0x24,0x1f,0x79,0x99,0x61,0x10,0xbb,0x3c,0xc9,0xc5,0x99,0xf9,0x84,0xcd,
- 0xe4,0xfb,0x8a,0xc9,0xce,0xd9,0x2a,0x28,0x1c,0x27,0x00,0x1f,0x7b,0x5f,0xf1,0x74,
- 0x7b,0x0d,0x09,0x58,0x3f,0x6b,0x43,0x33,0x76,0x52,0x61,0x24,0xa4,0x24,0x72,0x12,
- 0x48,0x4a,0x24,0x1f,0xe1,0x39,0x21,0x2b,0x85,0xe6,0xeb,0xfd,0xaa,0x1e,0x20,0xab,
- 0xc1,0x02,0x75,0xc7,0x2b,0xb2,0x20,0x16,0xba,0x15,0xcf,0x43,0x81,0xb2,0x17,0xc6,
- 0xbe,0xdf,0x33,0xaa,0x2e,0x42,0x05,0x19,0x68,0x9c,0xdc,0x75,0x4d,0xf7,0xdf,0x28,
- 0xa4,0xab,0xf4,0x3a,0x83,0xb7,0xf1,0x79,0x81,0xeb,0x1c,0x8d,0xce,0xd4,0xf2,0x9f,
- 0x87,0xfe,0x9d,0x02,0xff,0xd0,0x3f,0x44,0xff,0xbb,0x59,0x8e,0x4f,0x85,0xbe,0x96,
- 0x41,0x17,0xe1,0x6d,0x51,0x1f,0x73,0x6e,0x53,0x2d,0xe3,0xe2,0xf6,0xea,0x58,0x37,
- 0x57,0x1b,0x9e,0x88,0x4e,0x0b,0x3b,0x9e,0x78,0xf7,0x53,0xe8,0x6b,0xc5,0x49,0xc9,
- 0x16,0x2a,0x99,0x57,0x93,0xc2,0xf4,0xed,0xe8,0x75,0xb5,0x6c,0x1c,0x87,0xec,0x12,
- 0x7d,0xdf,0xb5,0x5d,0x51,0x4b,0xa3,0xaa,0xf1,0x36,0x30,0x58,0xd3,0x13,0x60,0xec,
- 0x51,0xbf,0x36,0xb5,0xe7,0x82,0xc1,0x42,0x41,0x14,0xaa,0xb9,0x64,0x74,0xb3,0xa2,
- 0xdc,0xc8,0x1b,0xe3,0x64,0xf6,0x0e,0x7d,0x57,0x1b,0x16,0x06,0xb3,0x48,0x61,0x59,
- 0xfb,0x74,0x71,0x39,0xa0,0x07,0xfd,0x1a,0x38,0x1d,0xef,0x33,0x5a,0xac,0x66,0x0f,
- 0x3d,0xb0,0x59,0x35,0x49,0xd5,0x3a,0xec,0x6f,0x9d,0xd6,0xdd,0xec,0x6c,0x99,0x89,
- 0x28,0xaa,0x9e,0xab,0x6d,0x8b,0x13,0xb2,0x01,0xe8,0x5d,0xbf,0x46,0x4e,0x77,0xab,
- 0x42,0x33,0x58,0xce,0xee,0xf9,0x6c,0xa2,0xb3,0x66,0x53,0x85,0xb1,0x2f,0x9d,0xcc,
- 0x1e,0xdb,0xca,0x77,0xe8,0x67,0xd2,0xc5,0x99,0xdc,0x5d,0x37,0x2b,0x8c,0x3d,0xa0,
- 0x37,0x4e,0xb7,0x45,0x20,0xd4,0x72,0x76,0xc7,0xe8,0x92,0x66,0x4e,0xc6,0xeb,0xa2,
- 0xe4,0x93,0x6c,0xcb,0x6f,0xa0,0xa7,0x6b,0xf4,0xb4,0x3b,0xf6,0x4e,0x70,0xae,0x59,
- 0xcd,0x92,0xe5,0x98,0xa1,0xfb,0x84,0xd3,0x4c,0x2f,0x12,0x98,0x7c,0x56,0xe8,0xef,
- 0xdf,0xfb,0x28,0xf9,0xc8,0x6f,0xdc,0x66,0xaa,0xec,0x02,0xc8,0xc6,0x29,0x7a,0x9b,
- 0x21,0xae,0x58,0x4d,0xb4,0xc9,0x22,0xcb,0x11,0x12,0x83,0xb5,0xb2,0xed,0x5c,0xf0,
- 0x4c,0x82,0x60,0xd6,0x43,0xdb,0x7e,0x79,0x80,0x9e,0xd0,0xc8,0x62,0xfc,0xc6,0x2e,
- 0x83,0x2d,0xa5,0x2c,0xc8,0x88,0xac,0x78,0x89,0x9f,0x5c,0xb0,0x1a,0xe5,0xf3,0x58,
- 0x90,0x36,0x43,0x48,0xcc,0x9a,0xe3,0x94,0xc0,0xaa,0xfc,0x71,0x36,0x86,0xd3,0x92,
- 0x8d,0x69,0x0b,0xfd,0xf6,0x46,0x7e,0x36,0x3e,0x92,0x5b,0xb4,0xd3,0xa5,0x8b,0x05,
- 0x1a,0xd5,0x8a,0xfc,0xd4,0x5d,0xed,0xa9,0x98,0x4c,0xda,0x5b,0xae,0xfd,0xe3,0xe8,
- 0xd1,0xf8,0xe8,0xa6,0xf4,0x7c,0xe9,0xdf,0xa3,0x8f,0xed,0xf8,0x2e,0xf4,0x68,0x7c,
- 0xb4,0x0a,0xa7,0x4b,0x17,0xe8,0x4d,0xd2,0x97,0x8c,0x76,0x1b,0x7d,0x6c,0xc7,0x6b,
- 0xe8,0xff,0x3c,0xd6,0xa4,0x8f,0x80,0x2c,0xa4,0xfe,0x86,0x6d,0x65,0x77,0xc0,0x60,
- 0xb3,0xf4,0x8f,0x53,0xd5,0xed,0x45,0x83,0x1d,0xc5,0xf8,0x8d,0x16,0x1d,0x5e,0x2a,
- 0xa3,0x3a,0xbc,0x30,0x23,0xca,0x56,0xb4,0x39,0xbe,0x24,0x6b,0x04,0xb4,0x2d,0x0c,
- 0xb5,0xfa,0x97,0x0d,0x17,0x84,0x2d,0x4b,0x16,0x03,0xfa,0xd9,0x27,0x61,0x89,0x5e,
- 0xd4,0xc8,0xe1,0xc9,0xe4,0x9e,0x0e,0x3e,0x05,0x0e,0x99,0xf6,0x1b,0xcf,0xd1,0xbb,
- 0xcf,0x58,0x51,0xa7,0x19,0xa8,0xe5,0x04,0xbd,0xe4,0x10,0x9f,0x83,0x5b,0xf4,0x5d,
- 0xc9,0xe7,0x80,0x9e,0x16,0xe8,0x27,0xc7,0x8b,0x32,0x3c,0x28,0x64,0xf7,0xd4,0xfa,
- 0xd0,0x37,0x75,0x15,0xb0,0x49,0x01,0xba,0x7c,0x70,0x05,0xcb,0x86,0xba,0x44,0x24,
- 0xf4,0xa2,0xab,0x8e,0x71,0x3c,0x7a,0x20,0xd4,0x32,0x54,0xbe,0x68,0x64,0x9c,0x28,
- 0xd2,0x3d,0xbf,0x17,0x52,0xc1,0xcd,0x50,0xae,0x24,0x5f,0x8d,0x69,0x66,0xad,0x13,
- 0x2f,0x0f,0xdc,0xdc,0x1f,0xcc,0x4a,0x76,0xa6,0x87,0xa3,0xa3,0x71,0x90,0xfe,0x0f,
- 0x5c,0x3e,0xc6,0xea,0xdd,0x74,0x11,0x07,0xb5,0x05,0xde,0x1b,0x82,0x71,0x24,0x25,
- 0x58,0xc3,0x2e,0x29,0x9a,0x95,0x29,0x7e,0xc3,0x1a,0x09,0x06,0xbc,0x2d,0xf4,0x2c,
- 0x96,0x19,0x05,0xc4,0x9b,0x92,0x4f,0xe6,0x8d,0x68,0x8f,0x89,0xe7,0x94,0x4a,0x7e,
- 0x91,0x42,0xab,0x90,0x0d,0xeb,0xb2,0x93,0x55,0xb0,0x05,0xc7,0x96,0x65,0x23,0x74,
- 0x04,0x5c,0xcd,0x77,0xff,0xb2,0xbe,0x71,0xcd,0xc9,0xc5,0x6f,0x6b,0xec,0x89,0x6e,
- 0x25,0x3f,0x6f,0x95,0x1b,0x7a,0x99,0x66,0x44,0xc1,0x57,0xa7,0x4e,0x93,0x7c,0xf3,
- 0xcd,0x6b,0x0b,0xa7,0x87,0xb5,0xdb,0xd0,0x7a,0xe1,0x45,0x1e,0xe4,0x46,0x57,0x33,
- 0x28,0x2b,0x7a,0xa9,0x8e,0x77,0xfc,0xf6,0x64,0xec,0x75,0x7a,0xb6,0x4d,0x82,0x31,
- 0x19,0xe9,0xdb,0x89,0x86,0x82,0x28,0xf9,0xd5,0x69,0xb4,0x28,0xf8,0x3c,0xbd,0xf7,
- 0xe2,0x4a,0x76,0x8d,0x7e,0x39,0xe7,0xcf,0x93,0x8f,0xf2,0x20,0x53,0xf7,0x8d,0x6c,
- 0x08,0x7a,0xab,0x6e,0xc7,0x7b,0x89,0xd4,0xfe,0xb9,0x8d,0x3e,0x4a,0xbe,0xd8,0x7f,
- 0x0b,0x30,0x14,0xa5,0x1f,0x32,0xd7,0x85,0x59,0xef,0x5d,0xf4,0xea,0x16,0xeb,0xf6,
- 0xda,0xba,0xf2,0x86,0xe3,0x7d,0xf4,0xa6,0xa6,0x35,0x73,0x9a,0x35,0xbd,0xbd,0x8c,
- 0x49,0x09,0xcd,0x24,0x9e,0x9a,0x33,0x9b,0xb9,0xb3,0x0d,0x27,0x37,0x61,0x47,0xb2,
- 0xe2,0xf9,0xd1,0x07,0xd0,0x4b,0x2d,0x60,0xd9,0x09,0xc4,0xcc,0x88,0xda,0xa6,0xef,
- 0x56,0xd0,0x76,0xdc,0xac,0xd4,0x0c,0xf2,0xc6,0x56,0x79,0x6e,0x69,0xb8,0xda,0xf0,
- 0xe8,0x7d,0xf4,0xab,0x5a,0xae,0x94,0xf4,0xe7,0xba,0xde,0x55,0xc5,0x7f,0xe8,0xff,
- 0x4b,0xe8,0xff,0x58,0x0e,0x86,0x52,0x10,0xaa,0xfe,0x94,0xdd,0x14,0xec,0x9e,0xfa,
- 0xae,0xc3,0x2e,0x66,0x5f,0x72,0x2c,0x52,0xa6,0xa1,0xfd,0x87,0x28,0x6e,0x0c,0xe3,
- 0x46,0x49,0xf0,0xf7,0x26,0xa0,0x49,0xdc,0x12,0x7d,0x91,0x1f,0x64,0x56,0x44,0xee,
- 0x42,0x94,0x8e,0x36,0x86,0x7f,0x8d,0x5e,0x75,0x70,0xb6,0xb8,0x1a,0x82,0x68,0x90,
- 0xb1,0xe9,0x5e,0x86,0x66,0xc7,0xe2,0x23,0xaf,0xf1,0x01,0xc9,0x7b,0x18,0xb3,0xa8,
- 0x5a,0x02,0x81,0x2c,0xac,0x81,0x0d,0x11,0x3d,0x46,0x02,0x50,0xe8,0x28,0xb6,0xf8,
- 0x99,0x80,0x94,0x59,0xba,0x04,0x8e,0x1e,0xa1,0xaf,0x40,0x22,0x84,0xa1,0xcd,0xce,
- 0x19,0x83,0xd5,0xa9,0xdb,0xd3,0xf0,0x76,0x63,0x73,0x8c,0x18,0xe0,0xc3,0xd4,0xd1,
- 0x86,0xd2,0x8b,0xc8,0x44,0x3c,0x94,0x26,0xbb,0x76,0x5c,0x2e,0xd1,0xb3,0x4b,0x9c,
- 0xc0,0x6f,0x03,0x68,0x91,0x72,0xa6,0xa3,0xfb,0x25,0xff,0x24,0x26,0xcb,0x76,0xb4,
- 0x39,0x99,0x0f,0x1a,0x6b,0x70,0x8e,0x52,0xd0,0xae,0x28,0x8a,0xba,0x57,0xd4,0x96,
- 0x61,0x7b,0xfb,0xcd,0x94,0x41,0x54,0xd5,0x2d,0x8c,0x47,0xb5,0xd2,0xb1,0xb3,0x99,
- 0xb6,0xee,0xb9,0xc5,0xb7,0xc1,0xdf,0x37,0x89,0x9e,0xf2,0xdd,0x85,0xa3,0x1c,0x67,
- 0xda,0xaa,0x67,0x3e,0x40,0x6f,0x5e,0x98,0xa5,0xd5,0x13,0x17,0x54,0x16,0x65,0xce,
- 0xa9,0x2c,0x0f,0xd9,0x6b,0xb5,0xa2,0x44,0xca,0xbb,0x21,0x82,0xde,0xe3,0x52,0x18,
- 0xf7,0xa3,0x0a,0x69,0x18,0x4f,0x0d,0x1b,0xd0,0x61,0xaf,0x3a,0xa3,0xd7,0x2a,0x8a,
- 0xbf,0x61,0x43,0xd9,0x53,0x57,0x43,0xeb,0x30,0xeb,0xee,0x5b,0x85,0x67,0x1a,0x7b,
- 0x67,0x52,0x34,0xb9,0xa0,0x5a,0x34,0x88,0xb1,0x61,0xf1,0x76,0x13,0xa7,0x18,0x95,
- 0x48,0x88,0x12,0x9c,0xc7,0xde,0x26,0x43,0xe9,0x27,0xd8,0x80,0xf6,0x5d,0x5a,0xd6,
- 0x50,0xa7,0x02,0xa3,0x33,0xc2,0xc3,0x5c,0xb8,0x47,0x2f,0x6b,0xfd,0x76,0x54,0x3c,
- 0x9c,0xe8,0x2d,0xc9,0x9f,0x23,0x4a,0xfa,0x4c,0xc8,0xd5,0xd9,0x30,0xb3,0x9f,0xb9,
- 0x44,0xba,0x43,0x64,0x1f,0x2d,0xce,0xdb,0xbd,0x1e,0xf6,0x11,0xbd,0xdd,0xd1,0xd1,
- 0x34,0xbf,0xf7,0x90,0x0b,0xde,0xec,0xe1,0x66,0x7b,0x76,0xf4,0x0c,0x3d,0x27,0xb4,
- 0xac,0x32,0xec,0x63,0xaf,0xee,0x68,0x46,0x38,0xc3,0x56,0xd3,0x88,0xe7,0x0b,0x30,
- 0x31,0x0b,0x4c,0x86,0x81,0x26,0x3b,0x7a,0x70,0x51,0x3b,0x45,0x2f,0x9b,0x1d,0x22,
- 0x40,0xbe,0x84,0xe4,0x23,0xa1,0x7b,0x0f,0xa2,0x11,0x6b,0x70,0x4d,0x2b,0x3a,0x8a,
- 0xec,0x01,0x57,0x8c,0x24,0x27,0x9d,0xcd,0x81,0x02,0x31,0x8b,0x5f,0x86,0xfd,0x8b,
- 0x38,0xf6,0x6e,0xbe,0x09,0xdb,0x5d,0x0b,0x35,0x51,0x05,0x88,0x2c,0x30,0x2c,0x1e,
- 0xa9,0xc7,0xe2,0x83,0x98,0xac,0x2b,0xaf,0x89,0xc6,0x97,0xf6,0x8a,0x33,0xf4,0xab,
- 0xc2,0xda,0x12,0x52,0xf2,0x1d,0x41,0x1d,0xe4,0x04,0x3d,0x11,0xc6,0x65,0xc5,0xa3,
- 0x8d,0xcd,0xad,0x8c,0x7e,0x1e,0xa6,0xa5,0x2e,0xfe,0x8f,0xd0,0xa3,0x99,0xc3,0x03,
- 0x1a,0xef,0xd0,0x9f,0x1c,0x6d,0x6c,0x6e,0x5d,0x6d,0x2d,0x7d,0x3f,0xfa,0x65,0x85,
- 0xac,0x2f,0xf9,0x43,0xf4,0x6a,0xea,0xf9,0x63,0x39,0x57,0xd6,0x0c,0xb6,0x50,0xf9,
- 0x90,0xd6,0xf4,0xed,0x81,0xe0,0x4a,0x45,0x18,0xeb,0x1f,0x1e,0x05,0x2d,0x16,0x62,
- 0x03,0xfc,0x8e,0xea,0xba,0x35,0x7f,0x93,0x60,0xbb,0x21,0xd3,0x70,0x9f,0x3c,0xe8,
- 0x0f,0x60,0x54,0x05,0xb6,0x7b,0xac,0xd2,0xbe,0x39,0x6d,0x31,0xc7,0x6e,0x36,0xd7,
- 0xc0,0xcc,0x19,0xbd,0xbb,0x90,0xb3,0x07,0x6b,0x1b,0x6c,0xdd,0xf2,0x92,0x3e,0xd0,
- 0x3b,0x4d,0x96,0x45,0x78,0x58,0xb7,0xcc,0x36,0x1b,0x32,0xa1,0x3f,0x79,0xd0,0x5b,
- 0x8e,0x31,0xe4,0xe4,0x5e,0x5a,0x8b,0xcf,0x40,0xdc,0xa5,0x11,0x3d,0x31,0x68,0xed,
- 0x67,0xc9,0xea,0x97,0x16,0x7e,0x31,0x5b,0x0d,0x10,0xe5,0x5a,0x62,0xc9,0xa2,0x27,
- 0x4b,0x49,0x29,0xe1,0xb9,0x4a,0x9b,0x14,0xd1,0xd1,0x3f,0x72,0x27,0xb3,0x32,0xa2,
- 0xcb,0x09,0x78,0xb8,0x4d,0x09,0x9b,0xe1,0x2a,0xfe,0x56,0x8f,0xcc,0x7a,0x18,0xa1,
- 0x55,0xb9,0x82,0xec,0x94,0x2d,0xb7,0x20,0xed,0x6f,0xb5,0x3a,0x88,0xb5,0x72,0xfb,
- 0xd9,0x14,0x30,0xa1,0x00,0xdd,0xfa,0x75,0x92,0xd0,0xc9,0x2f,0x87,0x2f,0x2e,0x3c,
- 0x68,0x80,0x45,0x86,0x57,0xae,0x53,0x0c,0xc8,0xd2,0x9f,0x0f,0xdd,0x8e,0x14,0x3d,
- 0xeb,0xd8,0xe7,0x30,0xb1,0x9d,0x34,0x47,0x65,0x99,0x3f,0xce,0x94,0xd0,0x55,0x1f,
- 0xec,0x45,0x68,0xe5,0x21,0x64,0x73,0xec,0xdc,0x42,0x8f,0xa1,0x4a,0xb2,0x5f,0xff,
- 0xb2,0xe4,0xeb,0xd8,0x97,0x29,0x5e,0x69,0x9d,0xec,0xa0,0xe7,0x47,0xc2,0x27,0x31,
- 0x75,0x22,0x3b,0x1b,0xe8,0xe1,0x93,0x0f,0xd2,0x6e,0x44,0xff,0x54,0xf2,0x29,0x7f,
- 0x3e,0xe0,0xc5,0x59,0x6f,0x1d,0x85,0x73,0x9b,0x8a,0x39,0x97,0x6b,0x0a,0xc2,0x3b,
- 0x4b,0xd1,0x02,0xc9,0xb6,0xe1,0x34,0xde,0x7b,0xf0,0x3b,0x7b,0x90,0x9a,0x2b,0x9b,
- 0xd3,0xa2,0x72,0x9e,0xac,0x7e,0xa7,0x70,0xa0,0xe9,0x1b,0x03,0x67,0xa9,0x19,0xf7,
- 0x39,0x85,0xeb,0x9c,0x3e,0xe0,0xc1,0x3a,0xc5,0x9f,0x2d,0xd4,0x5e,0x0f,0x88,0x20,
- 0xca,0x3a,0xc0,0x45,0xd2,0x76,0x55,0xe0,0x07,0x7e,0xec,0xf4,0x72,0xcb,0xfd,0xca,
- 0x9b,0xe8,0x91,0xbd,0xfe,0x3e,0xf4,0x77,0xb0,0x7f,0x3a,0xfa,0x3f,0x96,0x73,0x49,
- 0x2e,0x4c,0xdd,0x5e,0xb0,0x9c,0x4d,0x9e,0x21,0x9a,0x7e,0xa3,0xe0,0x6e,0x66,0x04,
- 0xc9,0xa9,0xd1,0x37,0x70,0x1c,0x74,0x66,0xdb,0x20,0x17,0xca,0x56,0x2e,0xd1,0x5f,
- 0xf3,0x0c,0xd9,0xed,0x77,0x2f,0x37,0x09,0x90,0x57,0xdd,0xa5,0x3c,0x22,0x5b,0xfc,
- 0x16,0xc7,0x41,0x67,0xb6,0x17,0x58,0x8e,0x2b,0x7b,0xfb,0xcf,0x8c,0x65,0x5a,0xbc,
- 0xdc,0x64,0xc7,0x4e,0xdc,0xa3,0x7a,0xdf,0x3e,0x21,0x59,0x66,0xa8,0x79,0x8d,0xe3,
- 0xe0,0x8e,0x17,0xc4,0xe4,0x3c,0x4b,0x9f,0xea,0x87,0x1e,0x2a,0x86,0x01,0x49,0xf8,
- 0x51,0x8f,0xdd,0xa2,0x2a,0x32,0x96,0xe7,0x1c,0x27,0xa2,0x0f,0x21,0x08,0xdb,0xa9,
- 0x13,0x13,0x5e,0x7c,0x42,0x6a,0xf1,0x89,0x89,0x84,0xde,0xe9,0xd6,0x43,0x3d,0x97,
- 0x3d,0x48,0x66,0x8f,0xe3,0x84,0xc0,0x92,0x09,0xfd,0xab,0x7a,0x7e,0xe4,0x78,0xf3,
- 0xf7,0x48,0x52,0x3a,0xa3,0xa7,0x47,0x8d,0x07,0xa5,0x5d,0xbf,0x0f,0xc3,0x9f,0x41,
- 0xff,0x82,0x9e,0xff,0xf8,0x99,0xcf,0xa1,0xb7,0x20,0x9c,0xf2,0x01,0xf4,0x2f,0x4b,
- 0xfe,0x0e,0x21,0x01,0xe3,0x6f,0x44,0x3f,0x7d,0xf2,0x60,0xb7,0x28,0x77,0x60,0xde,
- 0xe4,0x38,0x5c,0x13,0xab,0xd2,0x0b,0xe2,0xaf,0xb7,0x08,0xc2,0xb9,0x4d,0x8d,0x47,
- 0xd4,0x1d,0x62,0x12,0x02,0x66,0xd0,0xc1,0xec,0x3b,0x38,0x0e,0x7b,0xa4,0x57,0xb8,
- 0x90,0x7d,0x37,0xde,0x64,0x39,0x7b,0x1f,0xc6,0x6a,0xdf,0xac,0xe7,0xc6,0x70,0x3a,
- 0x4a,0x2b,0xde,0xe7,0x58,0xce,0xcf,0x44,0x8f,0x7b,0x10,0x9c,0x6c,0x3c,0x9f,0x64,
- 0x39,0x3f,0x13,0x3d,0x7e,0x3d,0xa4,0x24,0x1b,0xcf,0xef,0x66,0x39,0xff,0x03,0xca,
- 0x1b,0xaa,0xdf,0x6a,0xd1,0xba,0xa4,0x00,0x00,0x00,0x00,0x49,0x45,0x4e,0x44,0xae,
- 0x42,0x60,0x82 };
- int tigr_font_size = (int)sizeof(tigr_font);
- //////// End of inlined file: tigr_font.h ////////
- #include <stdlib.h>
- #include <stdarg.h>
- #include <errno.h>
- #include <stdio.h>
- #ifdef _MSC_VER
- #define vsnprintf _vsnprintf
- #endif
- TigrFont tigrStockFont;
- TigrFont* tfont = &tigrStockFont;
- // Converts 8-bit codepage entries into Unicode code points.
- static int cp1252[] = {
- 0x20ac, 0xfffd, 0x201a, 0x0192, 0x201e, 0x2026, 0x2020, 0x2021, 0x02c6, 0x2030, 0x0160, 0x2039, 0x0152,
- 0xfffd, 0x017d, 0xfffd, 0xfffd, 0x2018, 0x2019, 0x201c, 0x201d, 0x2022, 0x2013, 0x2014, 0x02dc, 0x2122,
- 0x0161, 0x203a, 0x0153, 0xfffd, 0x017e, 0x0178, 0x00a0, 0x00a1, 0x00a2, 0x00a3, 0x00a4, 0x00a5, 0x00a6,
- 0x00a7, 0x00a8, 0x00a9, 0x00aa, 0x00ab, 0x00ac, 0x00ad, 0x00ae, 0x00af, 0x00b0, 0x00b1, 0x00b2, 0x00b3,
- 0x00b4, 0x00b5, 0x00b6, 0x00b7, 0x00b8, 0x00b9, 0x00ba, 0x00bb, 0x00bc, 0x00bd, 0x00be, 0x00bf, 0x00c0,
- 0x00c1, 0x00c2, 0x00c3, 0x00c4, 0x00c5, 0x00c6, 0x00c7, 0x00c8, 0x00c9, 0x00ca, 0x00cb, 0x00cc, 0x00cd,
- 0x00ce, 0x00cf, 0x00d0, 0x00d1, 0x00d2, 0x00d3, 0x00d4, 0x00d5, 0x00d6, 0x00d7, 0x00d8, 0x00d9, 0x00da,
- 0x00db, 0x00dc, 0x00dd, 0x00de, 0x00df, 0x00e0, 0x00e1, 0x00e2, 0x00e3, 0x00e4, 0x00e5, 0x00e6, 0x00e7,
- 0x00e8, 0x00e9, 0x00ea, 0x00eb, 0x00ec, 0x00ed, 0x00ee, 0x00ef, 0x00f0, 0x00f1, 0x00f2, 0x00f3, 0x00f4,
- 0x00f5, 0x00f6, 0x00f7, 0x00f8, 0x00f9, 0x00fa, 0x00fb, 0x00fc, 0x00fd, 0x00fe, 0x00ff,
- };
- static int border(Tigr* bmp, int x, int y) {
- TPixel top = tigrGet(bmp, 0, 0);
- TPixel c = tigrGet(bmp, x, y);
- return (c.r == top.r && c.g == top.g && c.b == top.b) || x >= bmp->w || y >= bmp->h;
- }
- static void scan(Tigr* bmp, int* x, int* y, int* rowh) {
- while (*y < bmp->h) {
- if (*x >= bmp->w) {
- *x = 0;
- (*y) += *rowh;
- *rowh = 1;
- }
- if (!border(bmp, *x, *y))
- return;
- (*x)++;
- }
- }
- /*
- * Watermarks are encoded vertically in the alpha channel using seven pixels
- * starting at x, y. The first and last alpha values contain the magic values
- * 0b10101010 and 0b01010101 respectively.
- */
- static int readWatermark(Tigr* bmp, int x, int y, int* big, int* small) {
- const int magicHeader = 0xAA;
- const int magicFooter = 0x55;
- unsigned char watermark[7];
- for (int i = 0; i < 7; i++) {
- TPixel c = tigrGet(bmp, x, y + i);
- watermark[i] = c.a;
- }
- if (watermark[0] != magicHeader || watermark[6] != magicFooter) {
- return 0;
- }
- *big = watermark[1] | (watermark[2] << 8) | (watermark[3] << 16) | (watermark[4] << 24);
- *small = watermark[5];
- return 1;
- }
- int tigrLoadGlyphs(TigrFont* font, int codepage) {
- int x = 0;
- int y = 0;
- int w = 0;
- int h = 0;
- int rowh = 1;
- TigrGlyph* g;
- switch (codepage) {
- case TCP_ASCII:
- font->numGlyphs = 128 - 32;
- break;
- case TCP_1252:
- font->numGlyphs = 256 - 32;
- break;
- case TCP_UTF32:
- if (!readWatermark(font->bitmap, 0, 0, &font->numGlyphs, &rowh)) {
- return 0;
- }
- h = rowh;
- x = 1;
- break;
- default:
- errno = EINVAL;
- return 0;
- }
- font->glyphs = (TigrGlyph*)calloc(font->numGlyphs, sizeof(TigrGlyph));
- for (int index = 0; index < font->numGlyphs; index++) {
- // Look up the Unicode code point.
- g = &font->glyphs[index];
- if (codepage != TCP_UTF32) {
- // Find the next glyph.
- scan(font->bitmap, &x, &y, &rowh);
- if (y >= font->bitmap->h) {
- errno = EINVAL;
- return 0;
- }
- // Scan the width and height
- w = h = 0;
- while (!border(font->bitmap, x + w, y)) {
- w++;
- }
- while (!border(font->bitmap, x, y + h)) {
- h++;
- }
- }
- switch (codepage) {
- case TCP_ASCII:
- g->code = index + 32;
- break;
- case TCP_1252:
- if (index < 96) {
- g->code = index + 32;
- } else {
- g->code = cp1252[index - 96];
- }
- break;
- case TCP_UTF32:
- if (!readWatermark(font->bitmap, x, y, &g->code, &w)) {
- // Maybe we are at the end of a row?
- x = 0;
- y += rowh;
- if (!readWatermark(font->bitmap, x, y, &g->code, &w)) {
- return 0;
- }
- }
- x++;
- break;
- default:
- return 0;
- }
- g->x = x;
- g->y = y;
- g->w = w;
- g->h = h;
- x += w;
- if (h != font->glyphs[0].h) {
- errno = EINVAL;
- return 0;
- }
- if (h > rowh) {
- rowh = h;
- }
- }
- // Sort by code point.
- for (int i = 1; i < font->numGlyphs; i++) {
- int j = i;
- TigrGlyph g = font->glyphs[i];
- while (j > 0 && font->glyphs[j - 1].code > g.code) {
- font->glyphs[j] = font->glyphs[j - 1];
- j--;
- }
- font->glyphs[j] = g;
- }
- return 1;
- }
- TigrFont* tigrLoadFont(Tigr* bitmap, int codepage) {
- TigrFont* font = (TigrFont*)calloc(1, sizeof(TigrFont));
- font->bitmap = bitmap;
- if (!tigrLoadGlyphs(font, codepage)) {
- tigrFreeFont(font);
- return NULL;
- }
- return font;
- }
- void tigrFreeFont(TigrFont* font) {
- tigrFree(font->bitmap);
- free(font->glyphs);
- free(font);
- }
- static TigrGlyph* get(TigrFont* font, int code) {
- unsigned lo = 0, hi = font->numGlyphs;
- while (lo < hi) {
- unsigned guess = (lo + hi) / 2;
- if (code < font->glyphs[guess].code)
- hi = guess;
- else
- lo = guess + 1;
- }
- if (lo == 0 || font->glyphs[lo - 1].code != code)
- return &font->glyphs['?' - 32];
- else
- return &font->glyphs[lo - 1];
- }
- void tigrSetupFont(TigrFont* font) {
- // Load the stock font if needed.
- if (font == tfont && !tfont->bitmap) {
- tfont->bitmap = tigrLoadImageMem(tigr_font, tigr_font_size);
- tigrLoadGlyphs(tfont, 1252);
- }
- }
- void tigrPrint(Tigr* dest, TigrFont* font, int x, int y, TPixel color, const char* text, ...) {
- char tmp[1024];
- TigrGlyph* g;
- va_list args;
- const char* p;
- int start = x, c;
- tigrSetupFont(font);
- // Expand the formatting string.
- va_start(args, text);
- vsnprintf(tmp, sizeof(tmp), text, args);
- tmp[sizeof(tmp) - 1] = 0;
- va_end(args);
- // Print each glyph.
- p = tmp;
- while (*p) {
- p = tigrDecodeUTF8(p, &c);
- if (c == '\r')
- continue;
- if (c == '\n') {
- x = start;
- y += tigrTextHeight(font, "");
- continue;
- }
- g = get(font, c);
- tigrBlitTint(dest, font->bitmap, x, y, g->x, g->y, g->w, g->h, color);
- x += g->w;
- }
- }
- int tigrTextWidth(TigrFont* font, const char* text) {
- int x = 0, w = 0, c;
- tigrSetupFont(font);
- while (*text) {
- text = tigrDecodeUTF8(text, &c);
- if (c == '\n' || c == '\r') {
- x = 0;
- } else {
- x += get(font, c)->w;
- w = (x > w) ? x : w;
- }
- }
- return w;
- }
- int tigrTextHeight(TigrFont* font, const char* text) {
- int rowh, h, c;
- tigrSetupFont(font);
- h = rowh = get(font, 0)->h;
- while (*text) {
- text = tigrDecodeUTF8(text, &c);
- if (c == '\n' && *text)
- h += rowh;
- }
- return h;
- }
- //////// End of inlined file: tigr_print.c ////////
- //////// Start of inlined file: tigr_win.c ////////
- #ifndef TIGR_HEADLESS
- //#include "tigr_internal.h"
- #include <assert.h>
- // not really windows stuff
- TigrInternal* tigrInternal(Tigr* bmp) {
- assert(bmp->handle);
- return (TigrInternal*)(bmp + 1);
- }
- #ifdef _WIN32
- #define WIN32_LEAN_AND_MEAN
- #include <windows.h>
- #include <shellapi.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <stddef.h>
- #ifdef _MSC_VER
- #pragma comment(lib, "opengl32") // glViewport
- #pragma comment(lib, "shell32") // CommandLineToArgvW
- #pragma comment(lib, "user32") // SetWindowLong
- #pragma comment(lib, "gdi32") // ChoosePixelFormat
- #pragma comment(lib, "advapi32") // RegSetValueEx
- #endif
- #define WIDGET_SCALE 3
- #define WIDGET_FADE 16
- int main(int argc, char* argv[]);
- #ifndef TIGR_DO_NOT_PRESERVE_WINDOW_POSITION
- HKEY tigrRegKey;
- #endif
- #ifdef __TINYC__
- #define CP_UTF8 65001
- int WINAPI MultiByteToWideChar();
- int WINAPI WideCharToMultiByte();
- #endif
- static wchar_t* unicode(const char* str) {
- int len = MultiByteToWideChar(CP_UTF8, 0, str, -1, 0, 0);
- wchar_t* dest = (wchar_t*)malloc(sizeof(wchar_t) * len);
- MultiByteToWideChar(CP_UTF8, 0, str, -1, dest, len);
- return dest;
- }
- void tigrError(Tigr* bmp, const char* message, ...) {
- char tmp[1024];
- va_list args;
- va_start(args, message);
- _vsnprintf(tmp, sizeof(tmp), message, args);
- tmp[sizeof(tmp) - 1] = 0;
- va_end(args);
- MessageBoxW(bmp ? (HWND)bmp->handle : NULL, unicode(tmp), bmp ? tigrInternal(bmp)->wtitle : L"Error",
- MB_OK | MB_ICONERROR);
- exit(1);
- }
- void tigrEnterBorderlessWindowed(Tigr* bmp) {
- // Enter borderless windowed mode.
- MONITORINFO mi = { sizeof(mi) };
- TigrInternal* win = tigrInternal(bmp);
- GetWindowRect((HWND)bmp->handle, &win->oldPos);
- GetMonitorInfo(MonitorFromWindow((HWND)bmp->handle, MONITOR_DEFAULTTONEAREST), &mi);
- win->dwStyle = WS_VISIBLE | WS_POPUP;
- SetWindowLong((HWND)bmp->handle, GWL_STYLE, win->dwStyle);
- SetWindowPos((HWND)bmp->handle, HWND_TOP, mi.rcMonitor.left, mi.rcMonitor.top,
- mi.rcMonitor.right - mi.rcMonitor.left, mi.rcMonitor.bottom - mi.rcMonitor.top, 0);
- }
- void tigrLeaveBorderlessWindowed(Tigr* bmp) {
- TigrInternal* win = tigrInternal(bmp);
- win->dwStyle = WS_VISIBLE | WS_OVERLAPPEDWINDOW;
- SetWindowLong((HWND)bmp->handle, GWL_STYLE, win->dwStyle);
- SetWindowPos((HWND)bmp->handle, NULL, win->oldPos.left, win->oldPos.top, win->oldPos.right - win->oldPos.left,
- win->oldPos.bottom - win->oldPos.top, 0);
- }
- void tigrWinUpdateWidgets(Tigr* bmp, int dw, int dh) {
- POINT pt;
- int i, x, clicked = 0;
- char str[8];
- TPixel col;
- TPixel off = tigrRGB(255, 255, 255);
- TPixel on = tigrRGB(0, 200, 255);
- TigrInternal* win = tigrInternal(bmp);
- (void)dh;
- tigrClear(win->widgets, tigrRGBA(0, 0, 0, 0));
- if (!(win->dwStyle & WS_POPUP)) {
- win->widgetsWanted = 0;
- win->widgetAlpha = 0;
- return;
- }
- // See if we want to be showing widgets or not.
- GetCursorPos(&pt);
- ScreenToClient((HWND)bmp->handle, &pt);
- if (pt.y == 0)
- win->widgetsWanted = 1;
- if (pt.y > win->widgets->h * WIDGET_SCALE)
- win->widgetsWanted = 0;
- // Track the alpha.
- if (win->widgetsWanted)
- win->widgetAlpha = (win->widgetAlpha <= 255 - WIDGET_FADE) ? win->widgetAlpha + WIDGET_FADE : 255;
- else
- win->widgetAlpha = (win->widgetAlpha >= WIDGET_FADE) ? win->widgetAlpha - WIDGET_FADE : 0;
- // Get relative coords.
- pt.x -= (dw - win->widgets->w * WIDGET_SCALE);
- pt.x /= WIDGET_SCALE;
- pt.y /= WIDGET_SCALE;
- tigrClear(win->widgets, tigrRGBA(0, 0, 0, win->widgetAlpha));
- // Render it.
- for (i = 0; i < 3; i++) {
- switch (i) {
- case 0:
- str[0] = '_';
- str[1] = 0;
- break; // "_" (minimize)
- case 1:
- str[0] = (char)0xEF;
- str[1] = (char)0xBF;
- str[2] = (char)0xBD;
- str[3] = 0;
- break; // "[]" (maximize)
- case 2:
- str[0] = (char)0xC3;
- str[1] = (char)0x97;
- str[2] = 0;
- break; // "x" (close)
- }
- x = win->widgets->w + (i - 3) * 12;
- if (i == 2)
- off = tigrRGB(255, 0, 0);
- if (pt.x >= x && pt.x < x + 10 && pt.y < win->widgets->h) {
- col = on;
- if (GetAsyncKeyState(VK_LBUTTON) & 0x8000)
- clicked |= 1 << i;
- } else {
- col = off;
- }
- col.a = win->widgetAlpha;
- tigrPrint(win->widgets, tfont, x, 2, col, str);
- }
- if (clicked & 1)
- ShowWindow((HWND)bmp->handle, SW_MINIMIZE);
- if (clicked & 2)
- tigrLeaveBorderlessWindowed(bmp);
- if (clicked & 4)
- SendMessage((HWND)bmp->handle, WM_CLOSE, 0, 0);
- }
- void tigrUpdate(Tigr* bmp) {
- MSG msg;
- RECT rc;
- int dw, dh;
- TigrInternal* win = tigrInternal(bmp);
- if (!win->shown) {
- win->shown = 1;
- UpdateWindow((HWND)bmp->handle);
- ShowWindow((HWND)bmp->handle, SW_SHOW);
- }
- // Get the window size.
- GetClientRect((HWND)bmp->handle, &rc);
- dw = rc.right - rc.left;
- dh = rc.bottom - rc.top;
- // Update the widget overlay.
- tigrWinUpdateWidgets(bmp, dw, dh);
- if (!tigrGAPIBegin(bmp)) {
- tigrGAPIPresent(bmp, dw, dh);
- SwapBuffers(win->gl.dc);
- tigrGAPIEnd(bmp);
- }
- memcpy(win->prev, win->keys, 256);
- win->scrollDeltaX = 0;
- win->scrollDeltaY = 0;
- // Run the message pump.
- while (PeekMessage(&msg, (HWND)bmp->handle, 0, 0, PM_REMOVE)) {
- if (msg.message == WM_QUIT)
- break;
- TranslateMessage(&msg);
- DispatchMessage(&msg);
- }
- }
- typedef BOOL(APIENTRY* PFNWGLSWAPINTERVALFARPROC_)(int);
- static PFNWGLSWAPINTERVALFARPROC_ wglSwapIntervalEXT_ = 0;
- int tigrGAPIBegin(Tigr* bmp) {
- TigrInternal* win = tigrInternal(bmp);
- return wglMakeCurrent(win->gl.dc, win->gl.hglrc) ? 0 : -1;
- }
- int tigrGAPIEnd(Tigr* bmp) {
- (void)bmp;
- return wglMakeCurrent(NULL, NULL) ? 0 : -1;
- }
- static BOOL UnadjustWindowRectEx(LPRECT prc, DWORD dwStyle, BOOL fMenu, DWORD dwExStyle) {
- BOOL fRc;
- RECT rc;
- SetRectEmpty(&rc);
- fRc = AdjustWindowRectEx(&rc, dwStyle, fMenu, dwExStyle);
- if (fRc) {
- prc->left -= rc.left;
- prc->top -= rc.top;
- prc->right -= rc.right;
- prc->bottom -= rc.bottom;
- }
- return fRc;
- }
- LRESULT CALLBACK tigrWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
- Tigr* bmp;
- TigrInternal* win = NULL;
- RECT rc;
- int dw, dh;
- GetClientRect(hWnd, &rc);
- dw = rc.right - rc.left;
- dh = rc.bottom - rc.top;
- bmp = (Tigr*)GetPropW(hWnd, L"Tigr");
- if (bmp)
- win = tigrInternal(bmp);
- switch (message) {
- case WM_PAINT:
- if (!tigrGAPIBegin(bmp)) {
- tigrGAPIPresent(bmp, dw, dh);
- SwapBuffers(win->gl.dc);
- tigrGAPIEnd(bmp);
- }
- ValidateRect(hWnd, NULL);
- break;
- case WM_CLOSE:
- if (win)
- win->closed = 1;
- break;
- case WM_GETMINMAXINFO:
- if (bmp) {
- MINMAXINFO* info = (MINMAXINFO*)lParam;
- RECT rc;
- rc.left = 0;
- rc.top = 0;
- if (win->flags & TIGR_AUTO) {
- rc.right = 32;
- rc.bottom = 32;
- } else {
- int minscale = tigrEnforceScale(1, win->flags);
- rc.right = bmp->w * minscale;
- rc.bottom = bmp->h * minscale;
- }
- AdjustWindowRectEx(&rc, win->dwStyle, FALSE, 0);
- info->ptMinTrackSize.x = rc.right - rc.left;
- info->ptMinTrackSize.y = rc.bottom - rc.top;
- }
- return 0;
- case WM_SIZING:
- if (win) {
- // Calculate scale-constrained sizes.
- RECT* rc = (RECT*)lParam;
- int dx, dy;
- UnadjustWindowRectEx(rc, win->dwStyle, FALSE, 0);
- dx = (rc->right - rc->left) % win->scale;
- dy = (rc->bottom - rc->top) % win->scale;
- switch (wParam) {
- case WMSZ_LEFT:
- rc->left += dx;
- break;
- case WMSZ_RIGHT:
- rc->right -= dx;
- break;
- case WMSZ_TOP:
- rc->top += dy;
- break;
- case WMSZ_TOPLEFT:
- rc->left += dx;
- rc->top += dy;
- break;
- case WMSZ_TOPRIGHT:
- rc->right -= dx;
- rc->top += dy;
- break;
- case WMSZ_BOTTOM:
- rc->bottom -= dy;
- break;
- case WMSZ_BOTTOMLEFT:
- rc->left += dx;
- rc->bottom -= dy;
- break;
- case WMSZ_BOTTOMRIGHT:
- rc->right -= dx;
- rc->bottom -= dy;
- break;
- }
- AdjustWindowRectEx(rc, win->dwStyle, FALSE, 0);
- }
- return TRUE;
- case WM_SIZE:
- if (win) {
- if (wParam != SIZE_MINIMIZED) {
- // Detect window size changes and update our bitmap accordingly.
- dw = LOWORD(lParam);
- dh = HIWORD(lParam);
- if (win->flags & TIGR_AUTO) {
- tigrResize(bmp, dw / win->scale, dh / win->scale);
- } else {
- win->scale = tigrEnforceScale(tigrCalcScale(bmp->w, bmp->h, dw, dh), win->flags);
- }
- tigrPosition(bmp, win->scale, dw, dh, win->pos);
- }
- // If someone tried to maximize us (e.g. via shortcut launch options),
- // prefer instead to be borderless.
- if (wParam == SIZE_MAXIMIZED) {
- ShowWindow((HWND)bmp->handle, SW_NORMAL);
- tigrEnterBorderlessWindowed(bmp);
- }
- }
- return 0;
- #ifndef TIGR_DO_NOT_PRESERVE_WINDOW_POSITION
- case WM_WINDOWPOSCHANGED: {
- // Save our position.
- WINDOWPLACEMENT wp = { sizeof(WINDOWPLACEMENT) };
- GetWindowPlacement(hWnd, &wp);
- if (win->dwStyle & WS_POPUP)
- wp.showCmd = SW_MAXIMIZE;
- RegSetValueExW(tigrRegKey, win->wtitle, 0, REG_BINARY, (BYTE*)&wp, sizeof(wp));
- return DefWindowProcW(hWnd, message, wParam, lParam);
- }
- #endif
- case WM_ACTIVATE:
- if (win) {
- memset(win->keys, 0, 256);
- memset(win->prev, 0, 256);
- win->lastChar = 0;
- }
- return 0;
- case WM_CHAR:
- if (win) {
- if (wParam == '\r') {
- wParam = '\n';
- }
- int repeating = (HIWORD(lParam) & KF_REPEAT) == KF_REPEAT;
- if (!repeating) {
- win->lastChar = wParam;
- }
- }
- return DefWindowProcW(hWnd, message, wParam, lParam);
- case WM_MENUCHAR:
- // Disable beep on Alt+Enter
- if (LOWORD(wParam) == VK_RETURN)
- return MNC_CLOSE << 16;
- return DefWindowProcW(hWnd, message, wParam, lParam);
- case WM_SYSKEYDOWN:
- if (win) {
- if (wParam == VK_RETURN) {
- // Alt+Enter
- if (win->dwStyle & WS_POPUP)
- tigrLeaveBorderlessWindowed(bmp);
- else
- tigrEnterBorderlessWindowed(bmp);
- return 0;
- }
- }
- // fall-thru
- case WM_KEYDOWN:
- if (win)
- win->keys[wParam] = 1;
- return DefWindowProcW(hWnd, message, wParam, lParam);
- case WM_SYSKEYUP:
- // fall-thru
- case WM_KEYUP:
- if (win)
- win->keys[wParam] = 0;
- return DefWindowProcW(hWnd, message, wParam, lParam);
- case WM_MOUSEWHEEL:
- if (win)
- win->scrollDeltaY += (float)GET_WHEEL_DELTA_WPARAM(wParam) / (float)WHEEL_DELTA;
- return DefWindowProcW(hWnd, message, wParam, lParam);
- case WM_MOUSEHWHEEL:
- if (win)
- win->scrollDeltaX += (float)GET_WHEEL_DELTA_WPARAM(wParam) / (float)WHEEL_DELTA;
- return DefWindowProcW(hWnd, message, wParam, lParam);
- default:
- return DefWindowProcW(hWnd, message, wParam, lParam);
- }
- return 0;
- }
- Tigr* tigrWindow(int w, int h, const char* title, int flags) {
- WNDCLASSEXW wcex = { 0 };
- int maxW, maxH, scale;
- HWND hWnd;
- DWORD dwStyle;
- RECT rc;
- Tigr* bmp;
- TigrInternal* win;
- #ifndef TIGR_DO_NOT_PRESERVE_WINDOW_POSITION
- WINDOWPLACEMENT wp;
- DWORD wpsize = sizeof(wp);
- #endif
- wchar_t* wtitle = unicode(title);
- // Find our registry key.
- #ifndef TIGR_DO_NOT_PRESERVE_WINDOW_POSITION
- RegCreateKeyExW(HKEY_CURRENT_USER, L"Software\\TIGR", 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL,
- &tigrRegKey, NULL);
- #endif
- // Register a window class.
- wcex.cbSize = sizeof(WNDCLASSEXW);
- wcex.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
- wcex.lpfnWndProc = tigrWndProc;
- wcex.hInstance = GetModuleHandle(NULL);
- wcex.hIcon = NULL;
- wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
- wcex.lpszClassName = L"TIGR";
- RegisterClassExW(&wcex);
- if (flags & TIGR_AUTO) {
- // Always use a 1:1 pixel size.
- scale = 1;
- } else {
- // See how big we can make it and still fit on-screen.
- maxW = GetSystemMetrics(SM_CXSCREEN) * 3 / 4;
- maxH = GetSystemMetrics(SM_CYSCREEN) * 3 / 4;
- scale = tigrCalcScale(w, h, maxW, maxH);
- }
- scale = tigrEnforceScale(scale, flags);
- // Get the final window size.
- dwStyle = WS_OVERLAPPEDWINDOW;
- rc.left = 0;
- rc.top = 0;
- rc.right = w * scale;
- rc.bottom = h * scale;
- AdjustWindowRect(&rc, dwStyle, FALSE);
- // Make a window.
- hWnd = CreateWindowW(L"TIGR", wtitle, dwStyle, CW_USEDEFAULT, CW_USEDEFAULT, rc.right - rc.left, rc.bottom - rc.top,
- NULL, NULL, wcex.hInstance, NULL);
- if (!hWnd)
- ExitProcess(1);
- if (flags & TIGR_NOCURSOR) {
- ShowCursor(FALSE);
- }
- // Wrap a bitmap around it.
- bmp = tigrBitmap2(w, h, sizeof(TigrInternal));
- bmp->handle = hWnd;
- // Set up the Windows parts.
- win = tigrInternal(bmp);
- win->dwStyle = dwStyle;
- win->wtitle = wtitle;
- win->shown = 0;
- win->closed = 0;
- win->scale = scale;
- win->lastChar = 0;
- win->flags = flags;
- win->p1 = win->p2 = win->p3 = 0;
- win->p4 = 1;
- win->widgetsWanted = 0;
- win->widgetAlpha = 0;
- win->widgetsScale = WIDGET_SCALE;
- win->widgets = tigrBitmap(40, 14);
- SetPropW(hWnd, L"Tigr", bmp);
- tigrGAPICreate(bmp);
- if (flags & TIGR_FULLSCREEN) {
- tigrEnterBorderlessWindowed(bmp);
- } else {
- // Try and restore our window position.
- #ifndef TIGR_DO_NOT_PRESERVE_WINDOW_POSITION
- if (RegQueryValueExW(tigrRegKey, wtitle, NULL, NULL, (BYTE*)&wp, &wpsize) == ERROR_SUCCESS) {
- if (wp.showCmd == SW_MAXIMIZE)
- tigrEnterBorderlessWindowed(bmp);
- else
- SetWindowPlacement(hWnd, &wp);
- }
- #endif
- }
- wglSwapIntervalEXT_ = (PFNWGLSWAPINTERVALFARPROC_)wglGetProcAddress("wglSwapIntervalEXT");
- if (wglSwapIntervalEXT_)
- wglSwapIntervalEXT_(1);
- return bmp;
- }
- void tigrFree(Tigr* bmp) {
- if (bmp->handle) {
- TigrInternal* win = tigrInternal(bmp);
- tigrGAPIDestroy(bmp);
- if (win->gl.hglrc && !wglDeleteContext(win->gl.hglrc)) {
- tigrError(bmp, "Cannot delete OpenGL context.\n");
- }
- win->gl.hglrc = NULL;
- if (win->gl.dc && !ReleaseDC((HWND)bmp->handle, win->gl.dc)) {
- tigrError(bmp, "Cannot release OpenGL device context.\n");
- }
- win->gl.dc = NULL;
- DestroyWindow((HWND)bmp->handle);
- free(win->wtitle);
- tigrFree(win->widgets);
- }
- free(bmp->pix);
- free(bmp);
- }
- int tigrClosed(Tigr* bmp) {
- TigrInternal* win = tigrInternal(bmp);
- int val = win->closed;
- win->closed = 0;
- return val;
- }
- float tigrTime(void) {
- static int first = 1;
- static LARGE_INTEGER prev;
- LARGE_INTEGER cnt, freq;
- ULONGLONG diff;
- QueryPerformanceCounter(&cnt);
- QueryPerformanceFrequency(&freq);
- if (first) {
- first = 0;
- prev = cnt;
- }
- diff = cnt.QuadPart - prev.QuadPart;
- prev = cnt;
- return (float)(diff / (double)freq.QuadPart);
- }
- void tigrMouse(Tigr* bmp, int* x, int* y, int* buttons) {
- POINT pt;
- TigrInternal* win;
- win = tigrInternal(bmp);
- GetCursorPos(&pt);
- ScreenToClient((HWND)bmp->handle, &pt);
- *x = (pt.x - win->pos[0]) / win->scale;
- *y = (pt.y - win->pos[1]) / win->scale;
- *buttons = 0;
- if (GetFocus() != bmp->handle)
- return;
- if (GetAsyncKeyState(VK_LBUTTON) & 0x8000)
- *buttons |= 1;
- if (GetAsyncKeyState(VK_MBUTTON) & 0x8000)
- *buttons |= 2;
- if (GetAsyncKeyState(VK_RBUTTON) & 0x8000)
- *buttons |= 4;
- }
- int tigrTouch(Tigr* bmp, TigrTouchPoint* points, int maxPoints) {
- int buttons = 0;
- if (maxPoints > 0) {
- tigrMouse(bmp, &points[0].x, &points[1].y, &buttons);
- }
- return buttons ? 1 : 0;
- }
- void tigrScrollWheel(Tigr* bmp, float* x, float* y) {
- TigrInternal* win;
- win = tigrInternal(bmp);
- if (x) {
- *x = win->scrollDeltaX;
- }
- if (y) {
- *y = win->scrollDeltaY;
- }
- }
- static int tigrWinVK(int key) {
- if (key >= 'A' && key <= 'Z')
- return key;
- if (key >= '0' && key <= '9')
- return key;
- switch (key) {
- case TK_BACKSPACE:
- return VK_BACK;
- case TK_TAB:
- return VK_TAB;
- case TK_RETURN:
- return VK_RETURN;
- case TK_SHIFT:
- return VK_SHIFT;
- case TK_CONTROL:
- return VK_CONTROL;
- case TK_ALT:
- return VK_MENU;
- case TK_PAUSE:
- return VK_PAUSE;
- case TK_CAPSLOCK:
- return VK_CAPITAL;
- case TK_ESCAPE:
- return VK_ESCAPE;
- case TK_SPACE:
- return VK_SPACE;
- case TK_PAGEUP:
- return VK_PRIOR;
- case TK_PAGEDN:
- return VK_NEXT;
- case TK_END:
- return VK_END;
- case TK_HOME:
- return VK_HOME;
- case TK_LEFT:
- return VK_LEFT;
- case TK_UP:
- return VK_UP;
- case TK_RIGHT:
- return VK_RIGHT;
- case TK_DOWN:
- return VK_DOWN;
- case TK_INSERT:
- return VK_INSERT;
- case TK_DELETE:
- return VK_DELETE;
- case TK_LWIN:
- return VK_LWIN;
- case TK_RWIN:
- return VK_RWIN;
- // case TK_APPS: return VK_APPS; // this key doesn't exist on OS X
- case TK_PAD0:
- return VK_NUMPAD0;
- case TK_PAD1:
- return VK_NUMPAD1;
- case TK_PAD2:
- return VK_NUMPAD2;
- case TK_PAD3:
- return VK_NUMPAD3;
- case TK_PAD4:
- return VK_NUMPAD4;
- case TK_PAD5:
- return VK_NUMPAD5;
- case TK_PAD6:
- return VK_NUMPAD6;
- case TK_PAD7:
- return VK_NUMPAD7;
- case TK_PAD8:
- return VK_NUMPAD8;
- case TK_PAD9:
- return VK_NUMPAD9;
- case TK_PADMUL:
- return VK_MULTIPLY;
- case TK_PADADD:
- return VK_ADD;
- case TK_PADENTER:
- return VK_SEPARATOR;
- case TK_PADSUB:
- return VK_SUBTRACT;
- case TK_PADDOT:
- return VK_DECIMAL;
- case TK_PADDIV:
- return VK_DIVIDE;
- case TK_F1:
- return VK_F1;
- case TK_F2:
- return VK_F2;
- case TK_F3:
- return VK_F3;
- case TK_F4:
- return VK_F4;
- case TK_F5:
- return VK_F5;
- case TK_F6:
- return VK_F6;
- case TK_F7:
- return VK_F7;
- case TK_F8:
- return VK_F8;
- case TK_F9:
- return VK_F9;
- case TK_F10:
- return VK_F10;
- case TK_F11:
- return VK_F11;
- case TK_F12:
- return VK_F12;
- case TK_NUMLOCK:
- return VK_NUMLOCK;
- case TK_SCROLL:
- return VK_SCROLL;
- case TK_LSHIFT:
- return VK_LSHIFT;
- case TK_RSHIFT:
- return VK_RSHIFT;
- case TK_LCONTROL:
- return VK_LCONTROL;
- case TK_RCONTROL:
- return VK_RCONTROL;
- case TK_LALT:
- return VK_LMENU;
- case TK_RALT:
- return VK_RMENU;
- case TK_SEMICOLON:
- return VK_OEM_1;
- case TK_EQUALS:
- return VK_OEM_PLUS;
- case TK_COMMA:
- return VK_OEM_COMMA;
- case TK_MINUS:
- return VK_OEM_MINUS;
- case TK_DOT:
- return VK_OEM_PERIOD;
- case TK_SLASH:
- return VK_OEM_2;
- case TK_BACKTICK:
- return VK_OEM_3;
- case TK_LSQUARE:
- return VK_OEM_4;
- case TK_BACKSLASH:
- return VK_OEM_5;
- case TK_RSQUARE:
- return VK_OEM_6;
- case TK_TICK:
- return VK_OEM_7;
- }
- return 0;
- }
- int tigrKeyDown(Tigr* bmp, int key) {
- TigrInternal* win;
- int k = tigrWinVK(key);
- if (GetFocus() != bmp->handle)
- return 0;
- win = tigrInternal(bmp);
- return win->keys[k] && !win->prev[k];
- }
- int tigrKeyHeld(Tigr* bmp, int key) {
- TigrInternal* win;
- int k = tigrWinVK(key);
- if (GetFocus() != bmp->handle)
- return 0;
- win = tigrInternal(bmp);
- return win->keys[k];
- }
- int tigrReadChar(Tigr* bmp) {
- TigrInternal* win = tigrInternal(bmp);
- int c = win->lastChar;
- win->lastChar = 0;
- return c;
- }
- // We supply our own WinMain and just chain through to the user's
- // real entry point.
- #ifdef UNICODE
- int CALLBACK wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLine, int nCmdShow)
- #else
- int CALLBACK WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
- #endif
- {
- int n, argc;
- LPWSTR* wargv = CommandLineToArgvW(GetCommandLineW(), &argc);
- char** argv = (char**)calloc(argc + 1, sizeof(int));
- (void)hInstance;
- (void)hPrevInstance;
- (void)lpCmdLine;
- (void)nCmdShow;
- for (n = 0; n < argc; n++) {
- int len = WideCharToMultiByte(CP_UTF8, 0, wargv[n], -1, 0, 0, NULL, NULL);
- argv[n] = (char*)malloc(len);
- WideCharToMultiByte(CP_UTF8, 0, wargv[n], -1, argv[n], len, NULL, NULL);
- }
- return main(argc, argv);
- }
- #endif
- #endif // #ifndef TIGR_HEADLESS
- //////// End of inlined file: tigr_win.c ////////
- //////// Start of inlined file: tigr_osx.c ////////
- #ifndef TIGR_HEADLESS
- // originally based on https://github.com/jimon/osx_app_in_plain_c
- //#include "tigr_internal.h"
- #if __MACOS__
- #pragma clang diagnostic push
- #pragma clang diagnostic ignored "-Wgnu-zero-variadic-macro-arguments"
- //////// Start of inlined file: tigr_objc.h ////////
- #ifndef TIGR_OBJC_H
- #define TIGR_OBJC_H
- #if defined(__IOS__) || defined(__MACOS__)
- #if defined(__OBJC__) && __has_feature(objc_arc)
- #error "Can't compile as objective-c code!"
- #endif
- // ABI is a bit different between platforms
- #ifdef __arm64__
- #define abi_objc_msgSend_stret objc_msgSend
- #else
- #define abi_objc_msgSend_stret objc_msgSend_stret
- #endif
- #ifdef __i386__
- #define abi_objc_msgSend_fpret objc_msgSend_fpret
- #else
- #define abi_objc_msgSend_fpret objc_msgSend
- #endif
- #define objc_msgSendSuper_t(RET, ...) ((RET(*)(struct objc_super*, SEL, ##__VA_ARGS__))objc_msgSendSuper)
- #define objc_msgSend_t(RET, ...) ((RET(*)(id, SEL, ##__VA_ARGS__))objc_msgSend)
- #define objc_msgSend_stret_t(RET, ...) ((RET(*)(id, SEL, ##__VA_ARGS__))abi_objc_msgSend_stret)
- #define objc_msgSend_id objc_msgSend_t(id)
- #define objc_msgSend_void objc_msgSend_t(void)
- #define objc_msgSend_void_id objc_msgSend_t(void, id)
- #define objc_msgSend_void_bool objc_msgSend_t(void, bool)
- #define sel(NAME) sel_registerName(NAME)
- #define class(NAME) ((id)objc_getClass(NAME))
- #define makeClass(NAME, SUPER) objc_allocateClassPair((Class)objc_getClass(SUPER), NAME, 0)
- // Check here to get the signature right: https://nshipster.com/type-encodings/
- #define addMethod(CLASS, NAME, IMPL, SIGNATURE) \
- if (!class_addMethod(CLASS, sel(NAME), (IMP)(IMPL), (SIGNATURE))) \
- assert(false)
- #define addIvar(CLASS, NAME, SIZE, SIGNATURE) \
- if (!class_addIvar(CLASS, NAME, SIZE, rint(log2(SIZE)), SIGNATURE)) \
- assert(false)
- #define objc_alloc(CLASS) objc_msgSend_id(class(CLASS), sel("alloc"))
- #if __LP64__ || NS_BUILD_32_LIKE_64
- #define NSIntegerEncoding "q"
- #define NSUIntegerEncoding "L"
- #else
- #define NSIntegerEncoding "i"
- #define NSUIntegerEncoding "I"
- #endif
- #endif // defined(__IOS__) || defined (__MACOS__)
- #endif // TIGR_OBJC_H
- //////// End of inlined file: tigr_objc.h ////////
- #pragma clang diagnostic pop
- #include <assert.h>
- #include <limits.h>
- #include <math.h>
- #include <stdarg.h>
- #include <stdbool.h>
- #include <stdint.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <CoreFoundation/CoreFoundation.h>
- #include <CoreGraphics/CoreGraphics.h>
- #include <mach/mach_time.h>
- #include <objc/NSObjCRuntime.h>
- #include <objc/message.h>
- #include <objc/objc.h>
- #include <objc/runtime.h>
- #include <CoreGraphics/CGBase.h>
- #include <CoreGraphics/CGGeometry.h>
- typedef CGPoint NSPoint;
- typedef CGSize NSSize;
- typedef CGRect NSRect;
- enum {
- NSKeyDown = 10,
- NSKeyDownMask = 1 << NSKeyDown,
- NSKeyUp = 11,
- NSKeyUpMask = 1 << NSKeyUp,
- };
- NSUInteger NSAllEventMask = NSUIntegerMax;
- extern id NSApp;
- extern id const NSDefaultRunLoopMode;
- #define NSApplicationActivationPolicyRegular 0
- bool terminated = false;
- static double _tigrTimestamp = 0;
- double _currentMediaTime(void) {
- uint64_t now = clock_gettime_nsec_np(CLOCK_UPTIME_RAW);
- double current = now * 1.0E-9;
- return current;
- }
- TigrInternal* _tigrInternalFromWindowDelegate(id wdg) {
- Tigr* bmp = 0;
- object_getInstanceVariable(wdg, "tigrHandle", (void**)&bmp);
- return bmp ? tigrInternal(bmp) : NULL;
- }
- TigrInternal* _tigrInternalFromWindow(id window) {
- if (!window)
- return NULL;
- id wdg = objc_msgSend_id(window, sel("delegate"));
- if (!wdg)
- return NULL;
- return _tigrInternalFromWindowDelegate(wdg);
- }
- NSUInteger applicationShouldTerminate(id self, SEL sel, id sender) {
- terminated = true;
- return 0;
- }
- void windowWillClose(id self, SEL _sel, id notification) {
- NSUInteger value = true;
- object_setInstanceVariable(self, "closed", (void*)value);
- object_setInstanceVariable(self, "tigrHandle", (void*)0);
- }
- void windowDidEnterFullScreen(id self, SEL _sel, id notification) {
- NSUInteger value = true;
- object_setInstanceVariable(self, "visible", (void*)value);
- }
- void windowDidResize(id self, SEL _sel, id notification) {
- TigrInternal* win;
- Tigr* bmp = 0;
- object_getInstanceVariable(self, "tigrHandle", (void**)&bmp);
- win = bmp ? tigrInternal(bmp) : NULL;
- if (win) {
- win->mouseButtons = 0;
- }
- }
- void windowDidBecomeKey(id self, SEL _sel, id notification) {
- TigrInternal* win;
- Tigr* bmp = 0;
- object_getInstanceVariable(self, "tigrHandle", (void**)&bmp);
- win = bmp ? tigrInternal(bmp) : NULL;
- if (win) {
- memset(win->keys, 0, 256);
- memset(win->prev, 0, 256);
- win->lastChar = 0;
- win->mouseButtons = 0;
- }
- }
- void onFrame(id self, SEL _sel, id displayLink) {
- double timestamp = objc_msgSend_t(double)(displayLink, sel("timestamp"));
- TigrInternal* win = _tigrInternalFromWindowDelegate(self);
- if (win == NULL) {
- return;
- }
- win->frameTime = timestamp;
- }
- void mouseEntered(id self, SEL _sel, id event) {
- id window = objc_msgSend_id(event, sel("window"));
- TigrInternal* win = _tigrInternalFromWindow(window);
- if (win) {
- win->mouseInView = 1;
- if (win->flags & TIGR_NOCURSOR) {
- objc_msgSend_id(class("NSCursor"), sel("hide"));
- }
- }
- }
- void mouseExited(id self, SEL _sel, id event) {
- id window = objc_msgSend_id(event, sel("window"));
- TigrInternal* win = _tigrInternalFromWindow(window);
- if (win) {
- win->mouseInView = 0;
- if (win->flags & TIGR_NOCURSOR) {
- objc_msgSend_id(class("NSCursor"), sel("unhide"));
- }
- }
- }
- bool _tigrIsWindowClosed(id window) {
- id wdg = objc_msgSend_id(window, sel("delegate"));
- if (!wdg)
- return false;
- NSUInteger value = 0;
- object_getInstanceVariable(wdg, "closed", (void**)&value);
- return value ? true : false;
- }
- bool _tigrIsWindowVisible(id window) {
- id wdg = objc_msgSend_id(window, sel("delegate"));
- if (!wdg)
- return false;
- NSUInteger value = 0;
- object_getInstanceVariable(wdg, "visible", (void**)&value);
- return value ? true : false;
- }
- static bool tigrOSXInited = false;
- static id autoreleasePool = NULL;
- #ifdef DEBUG
- static void _showPools(const char* context) {
- fprintf(stderr, "NSAutoreleasePool@%s:\n", context);
- objc_msgSend(class("NSAutoreleasePool"), sel("showPools"));
- }
- #define showPools(x) _showPools((x))
- #else
- #define showPools(x)
- #endif
- static id pushPool(void) {
- id pool = objc_msgSend_id(class("NSAutoreleasePool"), sel("alloc"));
- return objc_msgSend_id(pool, sel("init"));
- }
- static void popPool(id pool) {
- objc_msgSend_void(pool, sel("drain"));
- }
- void _tigrCleanupOSX(void) {
- showPools("cleanup");
- popPool(autoreleasePool);
- }
- void tigrInitOSX(void) {
- if (tigrOSXInited)
- return;
- atexit(&_tigrCleanupOSX);
- autoreleasePool = pushPool();
- showPools("init start");
- objc_msgSend_id(class("NSApplication"), sel("sharedApplication"));
- objc_msgSend_t(void, NSInteger)(NSApp, sel("setActivationPolicy:"), NSApplicationActivationPolicyRegular);
- Class appDelegateClass = makeClass("AppDelegate", "NSObject");
- addMethod(appDelegateClass, "applicationShouldTerminate", applicationShouldTerminate, NSUIntegerEncoding "@:@");
- id dgAlloc = objc_msgSend_id((id)appDelegateClass, sel("alloc"));
- id dg = objc_msgSend_id(dgAlloc, sel("init"));
- objc_msgSend_void_id(NSApp, sel("setDelegate:"), dg);
- objc_msgSend_void(NSApp, sel("finishLaunching"));
- id menuBar = objc_alloc("NSMenu");
- menuBar = objc_msgSend_id(menuBar, sel("init"));
- id appMenuItem = objc_alloc("NSMenuItem");
- appMenuItem = objc_msgSend_id(appMenuItem, sel("init"));
- objc_msgSend_void_id(menuBar, sel("addItem:"), appMenuItem);
- objc_msgSend_t(id, id)(NSApp, sel("setMainMenu:"), menuBar);
- id processInfo = objc_msgSend_id(class("NSProcessInfo"), sel("processInfo"));
- id appName = objc_msgSend_id(processInfo, sel("processName"));
- id appMenu = objc_alloc("NSMenu");
- appMenu = objc_msgSend_t(id, id)(appMenu, sel("initWithTitle:"), appName);
- id quitTitlePrefixString =
- objc_msgSend_t(id, const char*)(class("NSString"), sel("stringWithUTF8String:"), "Quit ");
- id quitTitle = objc_msgSend_t(id, id)(quitTitlePrefixString, sel("stringByAppendingString:"), appName);
- id quitMenuItemKey = objc_msgSend_t(id, const char*)(class("NSString"), sel("stringWithUTF8String:"), "q");
- id quitMenuItem = objc_alloc("NSMenuItem");
- quitMenuItem = objc_msgSend_t(id, id, SEL, id)(quitMenuItem, sel("initWithTitle:action:keyEquivalent:"), quitTitle,
- sel("terminate:"), quitMenuItemKey);
- objc_msgSend_void_id(appMenu, sel("addItem:"), quitMenuItem);
- objc_msgSend_void_id(appMenuItem, sel("setSubmenu:"), appMenu);
- tigrOSXInited = true;
- showPools("init end");
- }
- void tigrError(Tigr* bmp, const char* message, ...) {
- char tmp[1024];
- va_list args;
- va_start(args, message);
- vsnprintf(tmp, sizeof(tmp), message, args);
- tmp[sizeof(tmp) - 1] = 0;
- va_end(args);
- printf("tigr fatal error: %s\n", tmp);
- exit(1);
- }
- NSSize _tigrContentBackingSize(id window) {
- id contentView = objc_msgSend_id(window, sel("contentView"));
- NSRect rect = objc_msgSend_stret_t(NSRect)(contentView, sel("frame"));
- rect = objc_msgSend_stret_t(NSRect, NSRect)(contentView, sel("convertRectToBacking:"), rect);
- return rect.size;
- }
- enum {
- NSWindowStyleMaskTitled = 1 << 0,
- NSWindowStyleMaskClosable = 1 << 1,
- NSWindowStyleMaskMiniaturizable = 1 << 2,
- NSWindowStyleMaskResizable = 1 << 3,
- NSWindowStyleRegular = NSWindowStyleMaskTitled | NSWindowStyleMaskClosable | NSWindowStyleMaskMiniaturizable |
- NSWindowStyleMaskResizable,
- NSWindowStyleMaskFullSizeContentView = 1 << 15
- };
- Tigr* tigrWindow(int w, int h, const char* title, int flags) {
- Tigr* bmp;
- TigrInternal* win;
- tigrInitOSX();
- NSUInteger windowStyleMask = NSWindowStyleRegular & ~NSWindowStyleMaskMiniaturizable;
- // In AUTO mode, window follows requested size, unless downscaled by tigrEnforceScale below.
- int windowScale = 1;
- // In non-AUTO mode, see how big we can make it and still fit on-screen.
- if ((flags & TIGR_AUTO) == 0) {
- CGRect mainMonitor = CGDisplayBounds(CGMainDisplayID());
- int maxW = CGRectGetWidth(mainMonitor);
- int maxH = CGRectGetHeight(mainMonitor);
- NSRect screen = { { 0, 0 }, { maxW, maxH } };
- NSRect content = objc_msgSend_stret_t(NSRect, NSRect, NSUInteger)(
- class("NSWindow"), sel("contentRectForFrameRect:styleMask:"), screen, windowStyleMask);
- windowScale = tigrCalcScale(w, h, content.size.width, content.size.height);
- }
- windowScale = tigrEnforceScale(windowScale, flags);
- NSRect rect = { { 0, 0 }, { w * windowScale, h * windowScale } };
- id windowAlloc = objc_msgSend_id(class("NSWindow"), sel("alloc"));
- id window = ((id(*)(id, SEL, NSRect, NSUInteger, NSUInteger, BOOL))objc_msgSend)(
- windowAlloc, sel("initWithContentRect:styleMask:backing:defer:"), rect, windowStyleMask, 2, NO);
- objc_msgSend_void_bool(window, sel("setReleasedWhenClosed:"), NO);
- Class WindowDelegateClass = objc_allocateClassPair((Class)objc_getClass("NSObject"), "WindowDelegate", 0);
- addIvar(WindowDelegateClass, "closed", sizeof(NSUInteger), NSUIntegerEncoding);
- addIvar(WindowDelegateClass, "visible", sizeof(NSUInteger), NSUIntegerEncoding);
- addIvar(WindowDelegateClass, "tigrHandle", sizeof(void*), "ˆv");
- addMethod(WindowDelegateClass, "windowWillClose:", windowWillClose, "v@:@");
- addMethod(WindowDelegateClass, "windowDidEnterFullScreen:", windowDidEnterFullScreen, "v@:@");
- addMethod(WindowDelegateClass, "windowDidResize:", windowDidResize, "v@:@");
- addMethod(WindowDelegateClass, "windowDidBecomeKey:", windowDidBecomeKey, "v@:@");
- addMethod(WindowDelegateClass, "mouseEntered:", mouseEntered, "v@:@");
- addMethod(WindowDelegateClass, "mouseExited:", mouseExited, "v@:@");
- addMethod(WindowDelegateClass, "onFrame:", onFrame, "v@:@");
- id wdgAlloc = objc_msgSend_id((id)WindowDelegateClass, sel("alloc"));
- id wdg = objc_msgSend_id(wdgAlloc, sel("init"));
- if (flags & TIGR_FULLSCREEN) {
- objc_msgSend_void_id(window, sel("toggleFullScreen:"), window);
- if (flags & TIGR_NOCURSOR) {
- objc_msgSend_id(class("NSCursor"), sel("hide"));
- }
- } else {
- NSUInteger value = true;
- object_setInstanceVariable(wdg, "visible", (void*)value);
- }
- objc_msgSend_void_id(window, sel("setDelegate:"), wdg);
- id contentView = objc_msgSend_id(window, sel("contentView"));
- objc_msgSend_void_bool(contentView, sel("setWantsLayer:"), NO);
- bool useDisplayLink = objc_msgSend_t(bool, SEL)(class("NSView"), sel("instancesRespondToSelector:"),
- sel("displayLinkWithTarget:selector:"));
- if (useDisplayLink) {
- // Use display link to pace frames, if possible
- id link =
- objc_msgSend_t(id, id, SEL)(contentView, sel("displayLinkWithTarget:selector:"), wdg, sel("onFrame:"));
- id runLoop = objc_msgSend_id(class("NSRunLoop"), sel("currentRunLoop"));
- objc_msgSend_t(void, id, id)(link, sel("addToRunLoop:forMode:"), runLoop, NSDefaultRunLoopMode);
- objc_msgSend_void_bool(link, sel("setPaused:"), NO);
- }
- int wantsHighRes = (flags & TIGR_RETINA);
- objc_msgSend_void_bool(contentView, sel("setWantsBestResolutionOpenGLSurface:"), wantsHighRes);
- NSPoint point = { 20, 20 };
- ((void (*)(id, SEL, NSPoint))objc_msgSend)(window, sel("cascadeTopLeftFromPoint:"), point);
- id titleString = objc_msgSend_t(id, const char*)(class("NSString"), sel("stringWithUTF8String:"), title);
- objc_msgSend_void_id(window, sel("setTitle:"), titleString);
- uint32_t glAttributes[] = { 8, 24, // NSOpenGLPFAColorSize, 24,
- 11, 8, // NSOpenGLPFAAlphaSize, 8,
- 5, // NSOpenGLPFADoubleBuffer,
- 73, // NSOpenGLPFAAccelerated,
- // 72, // NSOpenGLPFANoRecovery,
- // 55, 1, // NSOpenGLPFASampleBuffers, 1,
- // 56, 4, // NSOpenGLPFASamples, 4,
- 99, 0x3200, // NSOpenGLPFAOpenGLProfile, NSOpenGLProfileVersion3_2Core,
- // 70, 0x00020400, // NSOpenGLPFARendererID, kCGLRendererGenericFloatID
- 0 };
- id pixelFormat = objc_alloc("NSOpenGLPixelFormat");
- pixelFormat = objc_msgSend_t(id, const uint32_t*)(pixelFormat, sel("initWithAttributes:"), glAttributes);
- objc_msgSend_void(pixelFormat, sel("autorelease"));
- id openGLContext = objc_alloc("NSOpenGLContext");
- openGLContext = objc_msgSend_t(id, id, id)(openGLContext, sel("initWithFormat:shareContext:"), pixelFormat, nil);
- objc_msgSend_void_id(openGLContext, sel("setView:"), contentView);
- objc_msgSend_void_id(window, sel("makeKeyAndOrderFront:"), window);
- objc_msgSend_void_bool(window, sel("setAcceptsMouseMovedEvents:"), YES);
- id blackColor = objc_msgSend_id(class("NSColor"), sel("blackColor"));
- objc_msgSend_void_id(window, sel("setBackgroundColor:"), blackColor);
- objc_msgSend_void_bool(NSApp, sel("activateIgnoringOtherApps:"), YES);
- NSSize windowContentSize = _tigrContentBackingSize(window);
- // In AUTO mode, always use a 1:1 pixel size, unless downscaled by tigrEnforceScale below.
- int bitmapScale = 1;
- // In non-AUTO mode, scale based on backing size
- if ((flags & TIGR_AUTO) == 0) {
- bitmapScale = tigrEnforceScale(tigrCalcScale(w, h, windowContentSize.width, windowContentSize.height), flags);
- } else {
- // In AUTO mode, bitmap size follows window size
- w = windowContentSize.width / windowScale;
- h = windowContentSize.height / windowScale;
- bitmapScale = tigrEnforceScale(bitmapScale, flags);
- }
- bmp = tigrBitmap2(w, h, sizeof(TigrInternal));
- bmp->handle = window;
- // Set the handle
- object_setInstanceVariable(wdg, "tigrHandle", (void*)bmp);
- {
- #define NSTrackingMouseEnteredAndExited 1
- #define NSTrackingActiveInKeyWindow 0x20
- #define NSTrackingInVisibleRect 0x200
- int trackingFlags = NSTrackingMouseEnteredAndExited | NSTrackingActiveInKeyWindow | NSTrackingInVisibleRect;
- id trackingArea = objc_msgSend_id(class("NSTrackingArea"), sel("alloc"));
- trackingArea = objc_msgSend_t(id, NSRect, int, id, id)(
- trackingArea, sel("initWithRect:options:owner:userInfo:"), rect, trackingFlags, wdg, 0);
- objc_msgSend_void_id(contentView, sel("addTrackingArea:"), trackingArea);
- }
- // Set up the Windows parts.
- win = tigrInternal(bmp);
- win->shown = 0;
- win->closed = 0;
- win->scale = bitmapScale;
- win->lastChar = 0;
- win->flags = flags;
- win->p1 = win->p2 = win->p3 = 0;
- win->p4 = 1;
- win->widgetsWanted = 0;
- win->widgetAlpha = 0;
- win->widgetsScale = 0;
- win->widgets = 0;
- win->gl.gl_legacy = 0;
- win->gl.glContext = openGLContext;
- win->mouseButtons = 0;
- win->mouseInView = 0;
- win->frameTime = 0;
- tigrPosition(bmp, win->scale, bmp->w, bmp->h, win->pos);
- GLint swapInterval = 1;
- NSInteger NSOpenGLCPSwapInterval = 222;
- objc_msgSend_t(void, GLint*, NSInteger)(openGLContext, sel("setValues:forParameter:"), &swapInterval,
- NSOpenGLCPSwapInterval);
- objc_msgSend_void(openGLContext, sel("makeCurrentContext"));
- tigrGAPICreate(bmp);
- return bmp;
- }
- void tigrFree(Tigr* bmp) {
- if (bmp->handle) {
- TigrInternal* win = tigrInternal(bmp);
- tigrGAPIDestroy(bmp);
- id window = (id)bmp->handle;
- if (!_tigrIsWindowClosed(window) && !terminated) {
- objc_msgSend_void(window, sel("close"));
- }
- if (win->flags & TIGR_NOCURSOR) {
- objc_msgSend_id(class("NSCursor"), sel("unhide"));
- }
- id wdg = objc_msgSend_id(window, sel("delegate"));
- objc_msgSend_void(wdg, sel("release"));
- objc_msgSend_void((id)win->gl.glContext, sel("release"));
- objc_msgSend_void(window, sel("release"));
- }
- free(bmp->pix);
- free(bmp);
- }
- uint8_t _tigrKeyFromOSX(uint16_t key) {
- // from Carbon HIToolbox/Events.h
- enum {
- kVK_ANSI_A = 0x00,
- kVK_ANSI_S = 0x01,
- kVK_ANSI_D = 0x02,
- kVK_ANSI_F = 0x03,
- kVK_ANSI_H = 0x04,
- kVK_ANSI_G = 0x05,
- kVK_ANSI_Z = 0x06,
- kVK_ANSI_X = 0x07,
- kVK_ANSI_C = 0x08,
- kVK_ANSI_V = 0x09,
- kVK_ANSI_B = 0x0B,
- kVK_ANSI_Q = 0x0C,
- kVK_ANSI_W = 0x0D,
- kVK_ANSI_E = 0x0E,
- kVK_ANSI_R = 0x0F,
- kVK_ANSI_Y = 0x10,
- kVK_ANSI_T = 0x11,
- kVK_ANSI_1 = 0x12,
- kVK_ANSI_2 = 0x13,
- kVK_ANSI_3 = 0x14,
- kVK_ANSI_4 = 0x15,
- kVK_ANSI_6 = 0x16,
- kVK_ANSI_5 = 0x17,
- kVK_ANSI_Equal = 0x18,
- kVK_ANSI_9 = 0x19,
- kVK_ANSI_7 = 0x1A,
- kVK_ANSI_Minus = 0x1B,
- kVK_ANSI_8 = 0x1C,
- kVK_ANSI_0 = 0x1D,
- kVK_ANSI_RightBracket = 0x1E,
- kVK_ANSI_O = 0x1F,
- kVK_ANSI_U = 0x20,
- kVK_ANSI_LeftBracket = 0x21,
- kVK_ANSI_I = 0x22,
- kVK_ANSI_P = 0x23,
- kVK_ANSI_L = 0x25,
- kVK_ANSI_J = 0x26,
- kVK_ANSI_Quote = 0x27,
- kVK_ANSI_K = 0x28,
- kVK_ANSI_Semicolon = 0x29,
- kVK_ANSI_Backslash = 0x2A,
- kVK_ANSI_Comma = 0x2B,
- kVK_ANSI_Slash = 0x2C,
- kVK_ANSI_N = 0x2D,
- kVK_ANSI_M = 0x2E,
- kVK_ANSI_Period = 0x2F,
- kVK_ANSI_Grave = 0x32,
- kVK_ANSI_KeypadDecimal = 0x41,
- kVK_ANSI_KeypadMultiply = 0x43,
- kVK_ANSI_KeypadPlus = 0x45,
- kVK_ANSI_KeypadClear = 0x47,
- kVK_ANSI_KeypadDivide = 0x4B,
- kVK_ANSI_KeypadEnter = 0x4C,
- kVK_ANSI_KeypadMinus = 0x4E,
- kVK_ANSI_KeypadEquals = 0x51,
- kVK_ANSI_Keypad0 = 0x52,
- kVK_ANSI_Keypad1 = 0x53,
- kVK_ANSI_Keypad2 = 0x54,
- kVK_ANSI_Keypad3 = 0x55,
- kVK_ANSI_Keypad4 = 0x56,
- kVK_ANSI_Keypad5 = 0x57,
- kVK_ANSI_Keypad6 = 0x58,
- kVK_ANSI_Keypad7 = 0x59,
- kVK_ANSI_Keypad8 = 0x5B,
- kVK_ANSI_Keypad9 = 0x5C,
- kVK_Return = 0x24,
- kVK_Tab = 0x30,
- kVK_Space = 0x31,
- kVK_Delete = 0x33,
- kVK_Escape = 0x35,
- kVK_Command = 0x37,
- kVK_Shift = 0x38,
- kVK_CapsLock = 0x39,
- kVK_Option = 0x3A,
- kVK_Control = 0x3B,
- kVK_RightShift = 0x3C,
- kVK_RightOption = 0x3D,
- kVK_RightControl = 0x3E,
- kVK_Function = 0x3F,
- kVK_F17 = 0x40,
- kVK_VolumeUp = 0x48,
- kVK_VolumeDown = 0x49,
- kVK_Mute = 0x4A,
- kVK_F18 = 0x4F,
- kVK_F19 = 0x50,
- kVK_F20 = 0x5A,
- kVK_F5 = 0x60,
- kVK_F6 = 0x61,
- kVK_F7 = 0x62,
- kVK_F3 = 0x63,
- kVK_F8 = 0x64,
- kVK_F9 = 0x65,
- kVK_F11 = 0x67,
- kVK_F13 = 0x69,
- kVK_F16 = 0x6A,
- kVK_F14 = 0x6B,
- kVK_F10 = 0x6D,
- kVK_F12 = 0x6F,
- kVK_F15 = 0x71,
- kVK_Help = 0x72,
- kVK_Home = 0x73,
- kVK_PageUp = 0x74,
- kVK_ForwardDelete = 0x75,
- kVK_F4 = 0x76,
- kVK_End = 0x77,
- kVK_F2 = 0x78,
- kVK_PageDown = 0x79,
- kVK_F1 = 0x7A,
- kVK_LeftArrow = 0x7B,
- kVK_RightArrow = 0x7C,
- kVK_DownArrow = 0x7D,
- kVK_UpArrow = 0x7E
- };
- switch (key) {
- case kVK_ANSI_Q:
- return 'Q';
- case kVK_ANSI_W:
- return 'W';
- case kVK_ANSI_E:
- return 'E';
- case kVK_ANSI_R:
- return 'R';
- case kVK_ANSI_T:
- return 'T';
- case kVK_ANSI_Y:
- return 'Y';
- case kVK_ANSI_U:
- return 'U';
- case kVK_ANSI_I:
- return 'I';
- case kVK_ANSI_O:
- return 'O';
- case kVK_ANSI_P:
- return 'P';
- case kVK_ANSI_A:
- return 'A';
- case kVK_ANSI_S:
- return 'S';
- case kVK_ANSI_D:
- return 'D';
- case kVK_ANSI_F:
- return 'F';
- case kVK_ANSI_G:
- return 'G';
- case kVK_ANSI_H:
- return 'H';
- case kVK_ANSI_J:
- return 'J';
- case kVK_ANSI_K:
- return 'K';
- case kVK_ANSI_L:
- return 'L';
- case kVK_ANSI_Z:
- return 'Z';
- case kVK_ANSI_X:
- return 'X';
- case kVK_ANSI_C:
- return 'C';
- case kVK_ANSI_V:
- return 'V';
- case kVK_ANSI_B:
- return 'B';
- case kVK_ANSI_N:
- return 'N';
- case kVK_ANSI_M:
- return 'M';
- case kVK_ANSI_0:
- return '0';
- case kVK_ANSI_1:
- return '1';
- case kVK_ANSI_2:
- return '2';
- case kVK_ANSI_3:
- return '3';
- case kVK_ANSI_4:
- return '4';
- case kVK_ANSI_5:
- return '5';
- case kVK_ANSI_6:
- return '6';
- case kVK_ANSI_7:
- return '7';
- case kVK_ANSI_8:
- return '8';
- case kVK_ANSI_9:
- return '9';
- case kVK_ANSI_Keypad0:
- return TK_PAD0;
- case kVK_ANSI_Keypad1:
- return TK_PAD1;
- case kVK_ANSI_Keypad2:
- return TK_PAD2;
- case kVK_ANSI_Keypad3:
- return TK_PAD3;
- case kVK_ANSI_Keypad4:
- return TK_PAD4;
- case kVK_ANSI_Keypad5:
- return TK_PAD5;
- case kVK_ANSI_Keypad6:
- return TK_PAD6;
- case kVK_ANSI_Keypad7:
- return TK_PAD7;
- case kVK_ANSI_Keypad8:
- return TK_PAD8;
- case kVK_ANSI_Keypad9:
- return TK_PAD9;
- case kVK_ANSI_KeypadMultiply:
- return TK_PADMUL;
- case kVK_ANSI_KeypadPlus:
- return TK_PADADD;
- case kVK_ANSI_KeypadEnter:
- return TK_PADENTER;
- case kVK_ANSI_KeypadMinus:
- return TK_PADSUB;
- case kVK_ANSI_KeypadDecimal:
- return TK_PADDOT;
- case kVK_ANSI_KeypadDivide:
- return TK_PADDIV;
- case kVK_F1:
- return TK_F1;
- case kVK_F2:
- return TK_F2;
- case kVK_F3:
- return TK_F3;
- case kVK_F4:
- return TK_F4;
- case kVK_F5:
- return TK_F5;
- case kVK_F6:
- return TK_F6;
- case kVK_F7:
- return TK_F7;
- case kVK_F8:
- return TK_F8;
- case kVK_F9:
- return TK_F9;
- case kVK_F10:
- return TK_F10;
- case kVK_F11:
- return TK_F11;
- case kVK_F12:
- return TK_F12;
- case kVK_Shift:
- return TK_LSHIFT;
- case kVK_Control:
- return TK_LCONTROL;
- case kVK_Option:
- return TK_LALT;
- case kVK_CapsLock:
- return TK_CAPSLOCK;
- case kVK_Command:
- return TK_LWIN;
- case kVK_Command - 1:
- return TK_RWIN;
- case kVK_RightShift:
- return TK_RSHIFT;
- case kVK_RightControl:
- return TK_RCONTROL;
- case kVK_RightOption:
- return TK_RALT;
- case kVK_Delete:
- return TK_BACKSPACE;
- case kVK_Tab:
- return TK_TAB;
- case kVK_Return:
- return TK_RETURN;
- case kVK_Escape:
- return TK_ESCAPE;
- case kVK_Space:
- return TK_SPACE;
- case kVK_PageUp:
- return TK_PAGEUP;
- case kVK_PageDown:
- return TK_PAGEDN;
- case kVK_End:
- return TK_END;
- case kVK_Home:
- return TK_HOME;
- case kVK_LeftArrow:
- return TK_LEFT;
- case kVK_UpArrow:
- return TK_UP;
- case kVK_RightArrow:
- return TK_RIGHT;
- case kVK_DownArrow:
- return TK_DOWN;
- case kVK_Help:
- return TK_INSERT;
- case kVK_ForwardDelete:
- return TK_DELETE;
- case kVK_F14:
- return TK_SCROLL;
- case kVK_F15:
- return TK_PAUSE;
- case kVK_ANSI_KeypadClear:
- return TK_NUMLOCK;
- case kVK_ANSI_Semicolon:
- return TK_SEMICOLON;
- case kVK_ANSI_Equal:
- return TK_EQUALS;
- case kVK_ANSI_Comma:
- return TK_COMMA;
- case kVK_ANSI_Minus:
- return TK_MINUS;
- case kVK_ANSI_Slash:
- return TK_SLASH;
- case kVK_ANSI_Backslash:
- return TK_BACKSLASH;
- case kVK_ANSI_Grave:
- return TK_BACKTICK;
- case kVK_ANSI_Quote:
- return TK_TICK;
- case kVK_ANSI_LeftBracket:
- return TK_LSQUARE;
- case kVK_ANSI_RightBracket:
- return TK_RSQUARE;
- case kVK_ANSI_Period:
- return TK_DOT;
- default:
- return 0;
- }
- }
- void _tigrOnCocoaEvent(id event, id window) {
- if (!event)
- return;
- TigrInternal* win = _tigrInternalFromWindow(window);
- if (!win) // just pipe the event
- {
- objc_msgSend_void_id(NSApp, sel("sendEvent:"), event);
- return;
- }
- NSUInteger eventType = objc_msgSend_t(NSUInteger)(event, sel("type"));
- switch (eventType) {
- case 1: // NSLeftMouseDown
- if (win->mouseInView) {
- win->mouseButtons |= 1;
- }
- break;
- case 2: // NSLeftMouseUp
- win->mouseButtons &= ~1;
- break;
- case 3: // NSRightMouseDown
- if (win->mouseInView) {
- win->mouseButtons |= 2;
- }
- break;
- case 4: // NSRightMouseUp
- win->mouseButtons &= ~2;
- break;
- case 25: // NSOtherMouseDown
- {
- // number == 2 is a middle button
- NSInteger number = objc_msgSend_t(NSInteger)(event, sel("buttonNumber"));
- if (number == 2 && win->mouseInView) {
- win->mouseButtons |= 4;
- }
- break;
- }
- case 26: // NSOtherMouseUp
- {
- NSInteger number = objc_msgSend_t(NSInteger)(event, sel("buttonNumber"));
- if (number == 2)
- win->mouseButtons &= ~4;
- break;
- }
- case 22: // NSScrollWheel
- {
- CGFloat deltaX = objc_msgSend_t(CGFloat)(event, sel("deltaX"));
- win->scrollDeltaX += deltaX;
- CGFloat deltaY = objc_msgSend_t(CGFloat)(event, sel("deltaY"));
- win->scrollDeltaY += deltaY;
- break;
- }
- case 12: // NSFlagsChanged
- {
- NSUInteger modifiers = objc_msgSend_t(NSUInteger)(event, sel("modifierFlags"));
- // based on NSEventModifierFlags and
- // NSDeviceIndependentModifierFlagsMask
- struct {
- union {
- struct {
- uint8_t alpha_shift : 1;
- uint8_t shift : 1;
- uint8_t control : 1;
- uint8_t alternate : 1;
- uint8_t command : 1;
- uint8_t numeric_pad : 1;
- uint8_t help : 1;
- uint8_t function : 1;
- };
- uint8_t mask;
- };
- } keys;
- keys.mask = (modifiers & 0xffff0000UL) >> 16;
- // TODO L,R variation of keys?
- win->keys[TK_CONTROL] = keys.alpha_shift;
- win->keys[TK_SHIFT] = keys.shift;
- win->keys[TK_CONTROL] = keys.control;
- win->keys[TK_ALT] = keys.alternate;
- win->keys[TK_LWIN] = keys.command;
- win->keys[TK_RWIN] = keys.command;
- break;
- }
- case 10: // NSKeyDown
- {
- uint16_t keyCode = objc_msgSend_t(unsigned short)(event, sel("keyCode"));
- int tigrKey = _tigrKeyFromOSX(keyCode);
- // Ignore keyboard repeats
- if (!win->keys[tigrKey]) {
- win->keys[tigrKey] = 1;
- id inputText = objc_msgSend_id(event, sel("characters"));
- const char* inputTextUTF8 = objc_msgSend_t(const char*)(inputText, sel("UTF8String"));
- int decoded = 0;
- tigrDecodeUTF8(inputTextUTF8, &decoded);
- if (decoded < 0xe000 || decoded > 0xf8ff) {
- win->lastChar = decoded;
- }
- }
- // Pass through cmd+key
- if (win->keys[TK_LWIN]) {
- break;
- }
- return;
- }
- case 11: // NSKeyUp
- {
- uint16_t keyCode = objc_msgSend_t(unsigned short)(event, sel("keyCode"));
- win->keys[_tigrKeyFromOSX(keyCode)] = 0;
- return;
- }
- default:
- break;
- }
- objc_msgSend_void_id(NSApp, sel("sendEvent:"), event);
- }
- void tigrUpdate(Tigr* bmp) {
- popPool(autoreleasePool);
- autoreleasePool = pushPool();
- TigrInternal* win;
- id openGLContext;
- id window;
- win = tigrInternal(bmp);
- window = (id)bmp->handle;
- openGLContext = (id)win->gl.glContext;
- if (terminated || _tigrIsWindowClosed(window)) {
- return;
- }
- id keyWindow = objc_msgSend_id(NSApp, sel("keyWindow"));
- unsigned long long eventMask = NSAllEventMask;
- if (keyWindow == window) {
- memcpy(win->prev, win->keys, 256);
- } else {
- eventMask &= ~(NSKeyDownMask | NSKeyUpMask);
- }
- win->scrollDeltaX = 0;
- win->scrollDeltaY = 0;
- id event = 0;
- BOOL visible = 0;
- double now = _currentMediaTime();
- double passed = now - _tigrTimestamp;
- do {
- event = objc_msgSend_t(id, NSUInteger, id, id, BOOL)(
- NSApp, sel("nextEventMatchingMask:untilDate:inMode:dequeue:"), eventMask, nil, NSDefaultRunLoopMode, YES);
- if (event != 0) {
- _tigrOnCocoaEvent(event, window);
- } else {
- visible = _tigrIsWindowVisible(window);
- }
- } while (event != 0 || !visible);
- // The event processing loop above blocks during resize, which causes updates to freeze
- // but real time keeps ticking. We pretend that the event processing took no time
- // to avoid huge jumps in tigrTime.
- if (_tigrTimestamp != 0) {
- _tigrTimestamp = _currentMediaTime() - passed;
- }
- objc_msgSend_void(NSApp, sel("updateWindows"));
- objc_msgSend_void(openGLContext, sel("update"));
- tigrGAPIBegin(bmp);
- NSSize windowSize = _tigrContentBackingSize(window);
- if (win->flags & TIGR_AUTO)
- tigrResize(bmp, windowSize.width / win->scale, windowSize.height / win->scale);
- else
- win->scale = tigrEnforceScale(tigrCalcScale(bmp->w, bmp->h, windowSize.width, windowSize.height), win->flags);
- tigrPosition(bmp, win->scale, windowSize.width, windowSize.height, win->pos);
- tigrGAPIPresent(bmp, windowSize.width, windowSize.height);
- objc_msgSend_void(openGLContext, sel("flushBuffer"));
- tigrGAPIEnd(bmp);
- id runLoop = objc_msgSend_id(class("NSRunLoop"), sel("currentRunLoop"));
- id until = objc_msgSend_t(id, float)(class("NSDate"), sel("dateWithTimeIntervalSinceNow:"), 0.005f);
- double lastFrameTime = win->frameTime;
- static const double cap = 1.0 / 60;
- while (win->frameTime <= lastFrameTime) {
- // Run the event loop for 5ms at a time..
- objc_msgSend_t(void, id, id)(runLoop, sel("runMode:beforeDate:"), NSDefaultRunLoopMode, until);
- // ..until we get a frame callback..
- if (win->frameTime > lastFrameTime) {
- break;
- }
- double now = _currentMediaTime();
- double passed = now - lastFrameTime;
- // ..or it's time to cap the wait at 60 fps
- if (passed > cap) {
- win->frameTime = now;
- break;
- }
- }
- }
- int tigrGAPIBegin(Tigr* bmp) {
- TigrInternal* win = tigrInternal(bmp);
- objc_msgSend_void((id)win->gl.glContext, sel("makeCurrentContext"));
- return 0;
- }
- int tigrGAPIEnd(Tigr* bmp) {
- (void)bmp;
- objc_msgSend_void(class("NSOpenGLContext"), sel("clearCurrentContext"));
- return 0;
- }
- int tigrClosed(Tigr* bmp) {
- return (terminated || _tigrIsWindowClosed((id)bmp->handle)) ? 1 : 0;
- }
- void tigrMouse(Tigr* bmp, int* x, int* y, int* buttons) {
- TigrInternal* win;
- id window;
- win = tigrInternal(bmp);
- window = (id)bmp->handle;
- id windowContentView = objc_msgSend_id(window, sel("contentView"));
- NSRect adjustFrame = objc_msgSend_stret_t(NSRect)(windowContentView, sel("frame"));
- // NSPoint is small enough to fit a register, so no need for
- // objc_msgSend_stret
- NSPoint p = objc_msgSend_t(NSPoint)(window, sel("mouseLocationOutsideOfEventStream"));
- // map input to content view rect
- if (p.x < 0)
- p.x = 0;
- else if (p.x > adjustFrame.size.width)
- p.x = adjustFrame.size.width;
- if (p.y < 0)
- p.y = 0;
- else if (p.y > adjustFrame.size.height)
- p.y = adjustFrame.size.height;
- // map input to pixels
- NSRect r = { p, { 0, 0 } };
- r = objc_msgSend_stret_t(NSRect, NSRect)(windowContentView, sel("convertRectToBacking:"), r);
- p = r.origin;
- p.x = (p.x - win->pos[0]) / win->scale;
- p.y = bmp->h - (p.y - win->pos[1]) / win->scale;
- if (x)
- *x = p.x;
- if (y)
- *y = p.y;
- if (buttons) {
- id keyWindow = objc_msgSend_id(NSApp, sel("keyWindow"));
- *buttons = keyWindow != bmp->handle ? 0 : win->mouseButtons;
- }
- }
- int tigrTouch(Tigr* bmp, TigrTouchPoint* points, int maxPoints) {
- int buttons = 0;
- if (maxPoints > 0) {
- tigrMouse(bmp, &points[0].x, &points[1].y, &buttons);
- }
- return buttons ? 1 : 0;
- }
- void tigrScrollWheel(Tigr* bmp, float* x, float* y) {
- TigrInternal* win;
- win = tigrInternal(bmp);
- if (x) {
- *x = win->scrollDeltaX;
- }
- if (y) {
- *y = win->scrollDeltaY;
- }
- }
- int tigrKeyDown(Tigr* bmp, int key) {
- TigrInternal* win;
- assert(key < 256);
- win = tigrInternal(bmp);
- return (win->keys[key] != 0) && (win->prev[key] == 0);
- }
- int tigrKeyHeld(Tigr* bmp, int key) {
- TigrInternal* win;
- assert(key < 256);
- win = tigrInternal(bmp);
- return win->keys[key];
- }
- int tigrReadChar(Tigr* bmp) {
- TigrInternal* win = tigrInternal(bmp);
- int c = win->lastChar;
- win->lastChar = 0;
- return c;
- }
- float tigrTime(void) {
- double elapsed = 0;
- double now = _currentMediaTime();
- if (_tigrTimestamp != 0) {
- elapsed = now - _tigrTimestamp;
- }
- _tigrTimestamp = now;
- return (float)elapsed;
- }
- #endif // __MACOS__
- #endif // #ifndef TIGR_HEADLESS
- //////// End of inlined file: tigr_osx.c ////////
- //////// Start of inlined file: tigr_ios.c ////////
- #ifndef TIGR_HEADLESS
- //#include "tigr_internal.h"
- //#include "tigr_objc.h"
- #ifdef __IOS__
- #include <CoreGraphics/CoreGraphics.h>
- #include <objc/message.h>
- #include <objc/objc.h>
- #include <objc/runtime.h>
- #include <dispatch/dispatch.h>
- #include <os/log.h>
- #include <time.h>
- #include <stdatomic.h>
- id makeNSString(const char* str) {
- return objc_msgSend_t(id, const char*)(class("NSString"), sel("stringWithUTF8String:"), str);
- }
- id joinNSStrings(id a, id b) {
- return objc_msgSend_t(id, id)(a, sel("stringByAppendingString:"), b);
- }
- const char* UTF8StringFromNSString(id a) {
- return objc_msgSend_t(const char*)(a, sel("UTF8String"));
- }
- extern id UIApplication;
- static int NSQualityOfServiceUserInteractive = 0x21;
- typedef struct {
- TigrTouchPoint points[MAX_TOUCH_POINTS];
- int numPoints;
- } InputState;
- typedef struct {
- int keyCode;
- int codePoint;
- } KeyEvent;
- enum {
- KBD_HIDDEN = 0,
- KBD_SHOWREQ,
- KBD_HIDEREQ,
- KBD_SHOWN,
- };
- /// Global state
- static struct {
- InputState inputState;
- id viewController;
- id view;
- id context;
- id frameCondition;
- int screenW;
- int screenH;
- double scaleFactor;
- double timeSinceLastDraw;
- int renderReadFd;
- int mainWriteFd;
- _Atomic(int) keyboardState;
- } gState = {
- .inputState = {
- .numPoints = 0,
- },
- .viewController = 0,
- .view = 0,
- .context = 0,
- .frameCondition = 0,
- .screenW = 0,
- .screenH = 0,
- .scaleFactor = 1,
- .timeSinceLastDraw = 0,
- .renderReadFd = 0,
- .mainWriteFd = 0,
- .keyboardState = ATOMIC_VAR_INIT(KBD_HIDDEN),
- };
- typedef enum {
- SET_INPUT,
- KEY_EVENT,
- } TigrMessage;
- typedef struct TigrMessageData {
- TigrMessage message;
- union {
- InputState inputState;
- KeyEvent keyEvent;
- };
- } TigrMessageData;
- static id autoreleasePool = NULL;
- void writeToRenderThread(const TigrMessageData* message) {
- if (write(gState.mainWriteFd, message, sizeof(TigrMessageData)) != sizeof(TigrMessageData)) {
- os_log_error(OS_LOG_DEFAULT, "Failed to write message to render thread: %{public}s", strerror(errno));
- }
- }
- int readFromMainThread(TigrMessageData* message) {
- int result = read(gState.renderReadFd, message, sizeof(TigrMessageData));
- if (result == -1 && errno == EAGAIN) {
- return 0;
- }
- if (result != sizeof(TigrMessageData)) {
- os_log_error(OS_LOG_DEFAULT, "Failed to read message from main thread: %{public}s", strerror(errno));
- return 0;
- }
- return 1;
- }
- void viewWillTransitionToSize(id self, SEL _sel, CGSize size, id transitionCoordinator) {
- // No animation, just set them
- gState.screenW = size.width * gState.scaleFactor;
- gState.screenH = size.height * gState.scaleFactor;
- struct objc_super super = {
- self,
- objc_getClass("GLKViewController"),
- };
- objc_msgSendSuper_t(void, CGSize, id)(&super, _sel, size, transitionCoordinator);
- }
- BOOL prefersStatusBarHidden(id self, SEL _sel) {
- return YES;
- }
- BOOL hasText(id self, SEL _sel) {
- return NO;
- }
- void tigrShowKeyboard(int show) {
- int expected = show ? KBD_HIDDEN : KBD_SHOWN;
- int desired = show ? KBD_SHOWREQ : KBD_HIDEREQ;
- atomic_compare_exchange_weak(&gState.keyboardState, &expected, desired);
- }
- void insertText(id self, SEL _sel, id text) {
- const char* inserted = UTF8StringFromNSString(text);
- int codePoint = 0;
- do {
- inserted = tigrDecodeUTF8(inserted, &codePoint);
- if (codePoint != 0) {
- KeyEvent event;
- event.codePoint = codePoint;
- event.keyCode = (codePoint < 128) ? codePoint : 0;
- TigrMessageData message = {
- .message = KEY_EVENT,
- .keyEvent = event,
- };
- writeToRenderThread(&message);
- }
- } while (*inserted != 0);
- }
- void deleteBackward(id self, SEL _sel) {
- KeyEvent event;
- event.codePoint = 0;
- event.keyCode = 8; // BS
- TigrMessageData message = { .message = KEY_EVENT,
- .keyEvent = {
- .codePoint = 0,
- .keyCode = 8,
- } };
- writeToRenderThread(&message);
- }
- BOOL canBecomeFirstResponder(id self, SEL _sel) {
- return YES;
- }
- BOOL canResignFirstResponder(id self, SEL _sel) {
- return YES;
- }
- enum RenderState { SWAPPED = 5150, RENDERED };
- BOOL didFinishLaunchingWithOptions(id self, SEL _sel, id application, id options) {
- id screen = objc_msgSend_id(class("UIScreen"), sel("mainScreen"));
- CGRect bounds = objc_msgSend_t(CGRect)(screen, sel("bounds"));
- CGSize size = bounds.size;
- id window = objc_alloc("UIWindow");
- window = objc_msgSend_t(id, CGRect)(window, sel("initWithFrame:"), bounds);
- Class ViewController = makeClass("TigrViewController", "GLKViewController");
- addMethod(ViewController, "viewWillTransitionToSize:withTransitionCoordinator:", viewWillTransitionToSize,
- "v@:{CGSize}@");
- addMethod(ViewController, "prefersStatusBarHidden", prefersStatusBarHidden, "c@:");
- id vc = objc_msgSend_t(id)((id)ViewController, sel("alloc"));
- vc = objc_msgSend_id(vc, sel("init"));
- gState.viewController = vc;
- objc_msgSend_t(void, int)(vc, sel("setPreferredFramesPerSecond:"), 60);
- int framesPerSecond = objc_msgSend_t(int)(vc, sel("framesPerSecond"));
- id context = objc_alloc("EAGLContext");
- static int kEAGLRenderingAPIOpenGLES3 = 3;
- context = objc_msgSend_t(id, int)(context, sel("initWithAPI:"), kEAGLRenderingAPIOpenGLES3);
- gState.context = context;
- Class View = makeClass("TigrView", "GLKView");
- addMethod(View, "insertText:", insertText, "v@:@");
- addMethod(View, "deleteBackward", deleteBackward, "v@:");
- addMethod(View, "hasText", hasText, "c@:");
- addMethod(View, "canBecomeFirstResponder", canBecomeFirstResponder, "c@:");
- addMethod(View, "canResignFirstResponder", canResignFirstResponder, "c@:");
- Protocol* UIKeyInput = objc_getProtocol("UIKeyInput");
- class_addProtocol(View, UIKeyInput);
- id view = objc_msgSend_id((id)View, sel("alloc"));
- view = objc_msgSend_t(id, CGRect, id)(view, sel("initWithFrame:context:"), bounds, context);
- gState.view = view;
- objc_msgSend_t(void, BOOL)(view, sel("setMultipleTouchEnabled:"), YES);
- objc_msgSend_t(void, id)(view, sel("setDelegate:"), self);
- objc_msgSend_t(void, id)(vc, sel("setView:"), view);
- objc_msgSend_t(void, id)(vc, sel("setDelegate:"), self);
- objc_msgSend_t(void, id)(window, sel("setRootViewController:"), vc);
- objc_msgSend_t(void)(window, sel("makeKeyAndVisible"));
- gState.scaleFactor = objc_msgSend_t(double)(view, sel("contentScaleFactor"));
- gState.screenW = size.width * gState.scaleFactor;
- gState.screenH = size.height * gState.scaleFactor;
- gState.frameCondition = objc_msgSend_t(id, int)(objc_alloc("NSConditionLock"), sel("initWithCondition:"), RENDERED);
- objc_msgSend_t(void, int)(gState.frameCondition, sel("lockWhenCondition:"), RENDERED);
- id renderThread = objc_msgSend_t(id, id, SEL, id)(objc_alloc("NSThread"), sel("initWithTarget:selector:object:"),
- self, sel("renderMain"), NULL);
- objc_msgSend_t(void, int)(renderThread, sel("setQualityOfService:"), NSQualityOfServiceUserInteractive);
- objc_msgSend_t(void, id)(renderThread, sel("setName:"), makeNSString("Tigr Render Thread"));
- objc_msgSend_void(renderThread, sel("start"));
- return YES;
- }
- void waitForFrame() {
- objc_msgSend_t(void, id)(class("EAGLContext"), sel("setCurrentContext:"), 0);
- objc_msgSend_t(void, int)(gState.frameCondition, sel("unlockWithCondition:"), RENDERED);
- objc_msgSend_t(void, int)(gState.frameCondition, sel("lockWhenCondition:"), SWAPPED);
- objc_msgSend_t(void, id)(class("EAGLContext"), sel("setCurrentContext:"), gState.context);
- }
- void processKeyboardRequest() {
- int showReq = KBD_SHOWREQ;
- int hideReq = KBD_HIDEREQ;
- if (atomic_compare_exchange_weak(&gState.keyboardState, &showReq, KBD_SHOWN)) {
- objc_msgSend_t(BOOL)(gState.view, sel("becomeFirstResponder"));
- } else if (atomic_compare_exchange_weak(&gState.keyboardState, &hideReq, KBD_HIDDEN)) {
- objc_msgSend_t(BOOL)(gState.view, sel("resignFirstResponder"));
- }
- }
- void drawInRect(id _self, SEL _sel, id view, CGRect rect) {
- gState.timeSinceLastDraw = objc_msgSend_t(double)(gState.viewController, sel("timeSinceLastDraw"));
- objc_msgSend_t(void, int)(gState.frameCondition, sel("unlockWithCondition:"), SWAPPED);
- objc_msgSend_t(void, int)(gState.frameCondition, sel("lockWhenCondition:"), RENDERED);
- objc_msgSend_t(void, id)(class("EAGLContext"), sel("setCurrentContext:"), gState.context);
- processKeyboardRequest();
- }
- extern void tigrMain();
- void renderMain(id _self, SEL _sel) {
- objc_msgSend_t(void, int)(gState.frameCondition, sel("lockWhenCondition:"), SWAPPED);
- objc_msgSend_t(void, id)(class("EAGLContext"), sel("setCurrentContext:"), gState.context);
- tigrMain();
- }
- enum {
- UITouchPhaseBegan,
- UITouchPhaseMoved,
- UITouchPhaseStationary,
- UITouchPhaseEnded,
- UITouchPhaseCancelled,
- };
- void touches(id self, SEL sel, id touches, id event) {
- id allTouches = objc_msgSend_t(id)(event, sel("allTouches"));
- id enumerator = objc_msgSend_t(id)(allTouches, sel("objectEnumerator"));
- id touch = 0;
- InputState input = {
- .numPoints = 0,
- };
- while ((touch = objc_msgSend_t(id)(enumerator, sel("nextObject")))) {
- CGPoint location = objc_msgSend_t(CGPoint, id)(touch, sel("locationInView:"), NULL);
- int phase = objc_msgSend_t(int)(touch, sel("phase"));
- switch (phase) {
- case UITouchPhaseBegan:
- case UITouchPhaseMoved:
- case UITouchPhaseStationary:
- input.points[input.numPoints].x = location.x * gState.scaleFactor;
- input.points[input.numPoints].y = location.y * gState.scaleFactor;
- input.numPoints++;
- break;
- }
- if (input.numPoints >= MAX_TOUCH_POINTS) {
- break;
- }
- }
- TigrMessageData message = {
- .message = SET_INPUT,
- .inputState = input,
- };
- writeToRenderThread(&message);
- }
- Class tigrAppDelegate() {
- static Class delegateClass = 0;
- if (delegateClass != 0) {
- return delegateClass;
- }
- id application = objc_msgSend_id(class("UIApplication"), sel("sharedApplication"));
- delegateClass = makeClass("TigrAppDelegate", "UIResponder");
- addMethod(delegateClass, "application:didFinishLaunchingWithOptions:", didFinishLaunchingWithOptions, "c@:@@");
- addMethod(delegateClass, "touchesBegan:withEvent:", touches, "v@:@@");
- addMethod(delegateClass, "touchesMoved:withEvent:", touches, "v@:@@");
- addMethod(delegateClass, "touchesEnded:withEvent:", touches, "v@:@@");
- addMethod(delegateClass, "touchesCancelled:withEvent:", touches, "v@:@@");
- addMethod(delegateClass, "glkView:drawInRect:", drawInRect, "v@:@{CGRect}");
- addMethod(delegateClass, "renderMain", renderMain, "v@:");
- objc_registerClassPair(delegateClass);
- int fds[2];
- if (pipe(fds) != 0) {
- tigrError(0, "Failed to create message pipe");
- }
- int flags = fcntl(fds[0], F_GETFL, 0);
- fcntl(fds[0], F_SETFL, flags | O_NONBLOCK);
- gState.renderReadFd = fds[0];
- gState.mainWriteFd = fds[1];
- return delegateClass;
- }
- Tigr* tigrWindow(int w, int h, const char* title, int flags) {
- int scale = 1;
- if (flags & TIGR_AUTO) {
- // Always use a 1:1 pixel size.
- scale = 1;
- } else {
- // See how big we can make it and still fit on-screen.
- scale = tigrCalcScale(w, h, gState.screenW, gState.screenH);
- }
- scale = tigrEnforceScale(scale, flags);
- Tigr* bmp = tigrBitmap2(w, h, sizeof(TigrInternal));
- bmp->handle = (void*)4711;
- TigrInternal* win = tigrInternal(bmp);
- win->shown = 0;
- win->closed = 0;
- win->scale = scale;
- win->lastChar = 0;
- win->flags = flags;
- win->p1 = win->p2 = win->p3 = 0;
- win->p4 = 1;
- win->widgetsWanted = 0;
- win->widgetAlpha = 0;
- win->widgetsScale = 0;
- win->widgets = 0;
- win->gl.gl_legacy = 0;
- tigrPosition(bmp, win->scale, bmp->w, bmp->h, win->pos);
- tigrGAPICreate(bmp);
- return bmp;
- }
- void processEvents(TigrInternal* win) {
- memset(win->keys, 0, 255);
- TigrMessageData data;
- while (readFromMainThread(&data)) {
- switch (data.message) {
- case SET_INPUT:
- gState.inputState = data.inputState;
- break;
- case KEY_EVENT:
- win->keys[data.keyEvent.keyCode] = 1;
- win->lastChar = data.keyEvent.codePoint;
- break;
- }
- }
- }
- static int toWindowX(TigrInternal* win, int x) {
- return (x - win->pos[0]) / win->scale;
- }
- static int toWindowY(TigrInternal* win, int y) {
- return (y - win->pos[1]) / win->scale;
- }
- void tigrUpdate(Tigr* bmp) {
- TigrInternal* win = tigrInternal(bmp);
- processEvents(win);
- win->numTouchPoints = gState.inputState.numPoints;
- for (int i = 0; i < win->numTouchPoints; i++) {
- win->touchPoints[i].x = toWindowX(win, gState.inputState.points[i].x);
- win->touchPoints[i].y = toWindowY(win, gState.inputState.points[i].y);
- }
- win->mouseButtons = win->numTouchPoints;
- if (win->mouseButtons > 0) {
- win->mouseX = win->touchPoints[0].x;
- win->mouseY = win->touchPoints[0].y;
- }
- if (win->flags & TIGR_AUTO) {
- tigrResize(bmp, gState.screenW / win->scale, gState.screenH / win->scale);
- } else {
- win->scale = tigrEnforceScale(tigrCalcScale(bmp->w, bmp->h, gState.screenW, gState.screenH), win->flags);
- }
- tigrPosition(bmp, win->scale, gState.screenW, gState.screenH, win->pos);
- tigrGAPIPresent(bmp, gState.screenW, gState.screenH);
- waitForFrame();
- }
- int tigrClosed(Tigr* bmp) {
- return 0;
- }
- void tigrError(Tigr* bmp, const char* message, ...) {
- char tmp[1024];
- va_list args;
- va_start(args, message);
- vsnprintf(tmp, sizeof(tmp), message, args);
- tmp[sizeof(tmp) - 1] = 0;
- va_end(args);
- os_log_error(OS_LOG_DEFAULT, "tigr fatal error: %{public}s\n", tmp);
- exit(1);
- }
- float tigrTime() {
- return (float)gState.timeSinceLastDraw;
- }
- void tigrMouse(Tigr* bmp, int* x, int* y, int* buttons) {
- TigrInternal* win = tigrInternal(bmp);
- if (x) {
- *x = win->mouseX;
- }
- if (y) {
- *y = win->mouseY;
- }
- if (buttons) {
- *buttons = win->mouseButtons;
- }
- }
- int tigrTouch(Tigr* bmp, TigrTouchPoint* points, int maxPoints) {
- TigrInternal* win = tigrInternal(bmp);
- for (int i = 0; i < maxPoints && i < win->numTouchPoints; i++) {
- points[i] = win->touchPoints[i];
- }
- return maxPoints < win->numTouchPoints ? maxPoints : win->numTouchPoints;
- }
- void tigrMouseWheel(Tigr* bmp, float* x, float* y) {
- if (x) {
- *x = 0;
- }
- if (y) {
- *y = 0;
- }
- }
- void tigrFree(Tigr* bmp) {
- if (bmp->handle) {
- TigrInternal* win = tigrInternal(bmp);
- }
- free(bmp->pix);
- free(bmp);
- }
- int tigrGAPIBegin(Tigr* bmp) {
- (void)bmp;
- return 0;
- }
- int tigrGAPIEnd(Tigr* bmp) {
- (void)bmp;
- return 0;
- }
- int tigrKeyDown(Tigr* bmp, int key) {
- TigrInternal* win;
- assert(key < 256);
- win = tigrInternal(bmp);
- return win->keys[key];
- }
- int tigrKeyHeld(Tigr* bmp, int key) {
- return tigrKeyDown(bmp, key);
- }
- int tigrReadChar(Tigr* bmp) {
- TigrInternal* win = tigrInternal(bmp);
- int c = win->lastChar;
- win->lastChar = 0;
- return c;
- }
- extern void* _tigrReadFile(const char* fileName, int* length);
- void* tigrReadFile(const char* fileName, int* length) {
- id mainBundle = objc_msgSend_id(class("NSBundle"), sel("mainBundle"));
- id resourcePath = objc_msgSend_id(mainBundle, sel("resourcePath"));
- resourcePath = joinNSStrings(resourcePath, makeNSString("/"));
- resourcePath = joinNSStrings(resourcePath, makeNSString(fileName));
- return _tigrReadFile(UTF8StringFromNSString(resourcePath), length);
- }
- #endif // __IOS__
- #endif // #ifndef TIGR_HEADLESS
- //////// End of inlined file: tigr_ios.c ////////
- //////// Start of inlined file: tigr_android.h ////////
- #ifndef TIGR_ANDROID_H
- #define TIGR_ANDROID_H
- #ifdef __ANDROID__
- #include <android/input.h>
- #include <android/native_window.h>
- #include <EGL/egl.h>
- typedef enum {
- AE_INPUT,
- AE_WINDOW_CREATED,
- AE_WINDOW_DESTROYED,
- AE_RESUME,
- AE_CLOSE,
- } AndroidEventType;
- typedef struct {
- AndroidEventType type;
- AInputEvent* inputEvent;
- ANativeWindow* window;
- double time;
- } AndroidEvent;
- #ifdef __cplusplus
- extern "C" {
- #endif
- /// Calls from TIGR to Android side, render thread
- extern int android_pollEvent(int (*eventHandler)(AndroidEvent, void*), void*);
- extern void android_swap(EGLDisplay display, EGLSurface surface);
- extern void* android_loadAsset(const char* filename, int* outLength);
- extern void android_showKeyboard(int show);
- /// Calls from Android to TIGR side, main thread
- void tigr_android_create();
- void tigr_android_destroy();
- #ifdef __cplusplus
- }
- #endif
- #endif // __ANDROID__
- #endif // TIGR_ANDROID_H
- //////// End of inlined file: tigr_android.h ////////
- //////// Start of inlined file: tigr_linux.c ////////
- #ifndef TIGR_HEADLESS
- //#include "tigr_internal.h"
- #if __linux__ && !__ANDROID__
- #include <stdio.h>
- #include <stdlib.h>
- #include <stdbool.h>
- #include <string.h>
- #include <stdarg.h>
- #include <sys/time.h>
- #include <X11/X.h>
- #include <X11/Xlib.h>
- #include <X11/Xlocale.h>
- #include <X11/XKBlib.h>
- #include <X11/Xatom.h>
- #include <GL/glx.h>
- static Display* dpy;
- static Window root;
- static XVisualInfo* vi;
- static Atom wmDeleteMessage;
- static XIM inputMethod;
- static GLXFBConfig fbConfig;
- static PFNGLXCREATECONTEXTATTRIBSARBPROC glXCreateContextAttribsARB = 0;
- static void tearDownX11Stuff() {
- XCloseDisplay(dpy);
- }
- static void initX11Stuff() {
- static int done = 0;
- if (!done) {
- dpy = XOpenDisplay(NULL);
- if (dpy == NULL) {
- tigrError(0, "Cannot connect to X server");
- }
- root = DefaultRootWindow(dpy);
- static int attribList[] = { GLX_RENDER_TYPE,
- GLX_RGBA_BIT,
- GLX_DRAWABLE_TYPE,
- GLX_WINDOW_BIT,
- GLX_DOUBLEBUFFER,
- 1,
- GLX_RED_SIZE,
- 1,
- GLX_GREEN_SIZE,
- 1,
- GLX_BLUE_SIZE,
- 1,
- None };
- int fbcCount = 0;
- GLXFBConfig* fbc = glXChooseFBConfig(dpy, DefaultScreen(dpy), attribList, &fbcCount);
- if (!fbc) {
- tigrError(0, "Failed to choose FB config");
- }
- fbConfig = fbc[0];
- XFree(fbc);
- vi = glXGetVisualFromFBConfig(dpy, fbConfig);
- if (vi == NULL) {
- tigrError(0, "No appropriate visual found");
- }
- GLXContext tmpCtx = glXCreateContext(dpy, vi, 0, GL_TRUE);
- glXCreateContextAttribsARB =
- (PFNGLXCREATECONTEXTATTRIBSARBPROC)glXGetProcAddressARB((const GLubyte*)"glXCreateContextAttribsARB");
- glXDestroyContext(dpy, tmpCtx);
- if (!glXCreateContextAttribsARB) {
- tigrError(0, "Failed to get glXCreateContextAttribsARB");
- }
- inputMethod = XOpenIM(dpy, NULL, NULL, NULL);
- if (inputMethod == NULL) {
- tigrError(0, "Failed to create input method");
- }
- wmDeleteMessage = XInternAtom(dpy, "WM_DELETE_WINDOW", False);
- atexit(tearDownX11Stuff);
- done = 1;
- }
- }
- static int hasGLXExtension(Display* display, const char* wanted) {
- const char* extensions = glXQueryExtensionsString(display, DefaultScreen(display));
- char* dup = strdup(extensions);
- char* found = 0;
- for (char* start = dup;; start = 0) {
- found = strtok(start, " ");
- if (found == 0 || strcmp(found, wanted) == 0) {
- break;
- }
- }
- free(dup);
- return found != 0;
- }
- static void setupVSync(Display* display, Window win) {
- if (hasGLXExtension(display, "GLX_EXT_swap_control")) {
- PFNGLXSWAPINTERVALEXTPROC glXSwapIntervalEXT =
- (PFNGLXSWAPINTERVALEXTPROC)glXGetProcAddressARB((const GLubyte*)"glXSwapIntervalEXT");
- if (glXSwapIntervalEXT) {
- glXSwapIntervalEXT(display, win, 1);
- }
- } else if (hasGLXExtension(display, "GLX_MESA_swap_control")) {
- PFNGLXSWAPINTERVALMESAPROC glXSwapIntervalMESA =
- (PFNGLXSWAPINTERVALMESAPROC)glXGetProcAddressARB((const GLubyte*)"glXSwapIntervalMESA");
- if (glXSwapIntervalMESA) {
- glXSwapIntervalMESA(1);
- }
- } else if (hasGLXExtension(display, "GLX_SGI_swap_control")) {
- PFNGLXSWAPINTERVALSGIPROC glXSwapIntervalSGI =
- (PFNGLXSWAPINTERVALSGIPROC)glXGetProcAddressARB((const GLubyte*)"glXSwapIntervalSGI");
- if (glXSwapIntervalSGI) {
- glXSwapIntervalSGI(1);
- }
- }
- }
- static void tigrHideCursor(TigrInternal* win) {
- Cursor invisibleCursor;
- Pixmap bitmapNoData;
- XColor black;
- static char noData[] = { 0, 0, 0, 0, 0, 0, 0, 0 };
- black.red = black.green = black.blue = 0;
- bitmapNoData = XCreateBitmapFromData(win->dpy, win->win, noData, 8, 8);
- invisibleCursor = XCreatePixmapCursor(win->dpy, bitmapNoData, bitmapNoData, &black, &black, 0, 0);
- XDefineCursor(win->dpy, win->win, invisibleCursor);
- XFreeCursor(win->dpy, invisibleCursor);
- XFreePixmap(win->dpy, bitmapNoData);
- }
- typedef struct {
- unsigned long flags;
- unsigned long functions;
- unsigned long decorations;
- long inputMode;
- unsigned long status;
- } WindowHints;
- Tigr* tigrWindow(int w, int h, const char* title, int flags) {
- Tigr* bmp = 0;
- Colormap cmap;
- XSetWindowAttributes swa;
- Window xwin;
- GLXContext glc;
- XIC ic;
- int scale;
- initX11Stuff();
- if (flags & TIGR_AUTO) {
- // Always use a 1:1 pixel size, unless downscaled by tigrEnforceScale below.
- scale = 1;
- } else {
- // See how big we can make it and still fit on-screen.
- Screen* screen = DefaultScreenOfDisplay(dpy);
- int maxW = WidthOfScreen(screen);
- int maxH = HeightOfScreen(screen);
- scale = tigrCalcScale(w, h, maxW, maxH);
- }
- scale = tigrEnforceScale(scale, flags);
- cmap = XCreateColormap(dpy, root, vi->visual, AllocNone);
- swa.colormap = cmap;
- swa.event_mask = StructureNotifyMask;
- // Create window of wanted size
- xwin = XCreateWindow(dpy, root, 0, 0, w * scale, h * scale, 0, vi->depth, InputOutput, vi->visual,
- CWColormap | CWEventMask, &swa);
- XMapWindow(dpy, xwin);
- if (flags & TIGR_FULLSCREEN) {
- // https://superuser.com/questions/1680077/does-x11-actually-have-a-native-fullscreen-mode
- Atom wm_state = XInternAtom(dpy, "_NET_WM_STATE", true);
- Atom wm_fullscreen = XInternAtom(dpy, "_NET_WM_STATE_FULLSCREEN", true);
- XChangeProperty(dpy, xwin, wm_state, XA_ATOM, 32, PropModeReplace, (unsigned char*)&wm_fullscreen, 1);
- } else {
- // Wait for window to get mapped
- for (;;) {
- XEvent e;
- XNextEvent(dpy, &e);
- if (e.type == MapNotify) {
- break;
- }
- }
- // Reset size if we did not get the window size we wanted above.
- XWindowAttributes wa;
- XGetWindowAttributes(dpy, xwin, &wa);
- scale = tigrCalcScale(w, h, wa.width, wa.height);
- scale = tigrEnforceScale(scale, flags);
- XResizeWindow(dpy, xwin, w * scale, h * scale);
- }
- // Enable mouse events reporting (needed for mouse wheel events)
- XSelectInput(dpy, xwin, ButtonPressMask);
- XTextProperty prop;
- int result = Xutf8TextListToTextProperty(dpy, (char**)&title, 1, XUTF8StringStyle, &prop);
- if (result == Success) {
- Atom wmName = XInternAtom(dpy, "_NET_WM_NAME", 0);
- XSetTextProperty(dpy, xwin, &prop, wmName);
- XFree(prop.value);
- }
- ic = XCreateIC(inputMethod, XNInputStyle, XIMPreeditNothing | XIMStatusNothing, XNClientWindow, xwin, NULL);
- if (ic == NULL) {
- printf("Failed to create input context\n");
- exit(0);
- }
- XSetICFocus(ic);
- XSetWMProtocols(dpy, xwin, &wmDeleteMessage, 1);
- int contextAttributes[] = { GLX_CONTEXT_MAJOR_VERSION_ARB, 3, GLX_CONTEXT_MINOR_VERSION_ARB, 3, None };
- glc = glXCreateContextAttribsARB(dpy, fbConfig, NULL, GL_TRUE, contextAttributes);
- glXMakeCurrent(dpy, xwin, glc);
- setupVSync(dpy, xwin);
- bmp = tigrBitmap2(w, h, sizeof(TigrInternal));
- bmp->handle = (void*)xwin;
- TigrInternal* win = tigrInternal(bmp);
- win->win = xwin;
- win->dpy = dpy;
- win->glc = glc;
- win->ic = ic;
- win->shown = 0;
- win->closed = 0;
- win->scale = scale;
- win->lastChar = 0;
- win->flags = flags;
- win->p1 = win->p2 = win->p3 = 0;
- win->p4 = 1;
- win->widgetsWanted = 0;
- win->widgetAlpha = 0;
- win->widgetsScale = 0;
- win->widgets = 0;
- win->gl.gl_legacy = 0;
- memset(win->keys, 0, 256);
- memset(win->prev, 0, 256);
- if (flags & TIGR_NOCURSOR) {
- tigrHideCursor(win);
- }
- tigrPosition(bmp, win->scale, bmp->w, bmp->h, win->pos);
- tigrGAPICreate(bmp);
- tigrGAPIBegin(bmp);
- return bmp;
- }
- int tigrClosed(Tigr* bmp) {
- TigrInternal* win = tigrInternal(bmp);
- return win->win == 0;
- }
- int tigrGAPIBegin(Tigr* bmp) {
- TigrInternal* win = tigrInternal(bmp);
- return glXMakeCurrent(win->dpy, win->win, win->glc) ? 0 : -1;
- }
- int tigrGAPIEnd(Tigr* bmp) {
- (void)bmp;
- return glXMakeCurrent(NULL, 0, 0) ? 0 : -1;
- }
- int tigrKeyDown(Tigr* bmp, int key) {
- TigrInternal* win;
- assert(key < 256);
- win = tigrInternal(bmp);
- return win->keys[key] && !win->prev[key];
- }
- int tigrKeyHeld(Tigr* bmp, int key) {
- TigrInternal* win;
- assert(key < 256);
- win = tigrInternal(bmp);
- return win->keys[key];
- }
- int tigrReadChar(Tigr* bmp) {
- TigrInternal* win = tigrInternal(bmp);
- int c = win->lastChar;
- win->lastChar = 0;
- return c;
- }
- uint8_t tigrKeyFromX11(KeySym sym) {
- if (sym >= 'a' && sym <= 'z') {
- return (uint8_t)sym - ('a' - 'A');
- }
- if (sym >= '0' && sym <= '9') {
- return (uint8_t)sym;
- }
- switch (sym) {
- case XK_KP_0:
- return TK_PAD0;
- case XK_KP_1:
- return TK_PAD1;
- case XK_KP_2:
- return TK_PAD2;
- case XK_KP_3:
- return TK_PAD3;
- case XK_KP_4:
- return TK_PAD4;
- case XK_KP_5:
- return TK_PAD5;
- case XK_KP_6:
- return TK_PAD6;
- case XK_KP_7:
- return TK_PAD7;
- case XK_KP_8:
- return TK_PAD8;
- case XK_KP_9:
- return TK_PAD9;
- case XK_KP_Multiply:
- return TK_PADMUL;
- case XK_KP_Divide:
- return TK_PADDIV;
- case XK_KP_Add:
- return TK_PADADD;
- case XK_KP_Subtract:
- return TK_PADSUB;
- case XK_KP_Decimal:
- return TK_PADDOT;
- case XK_KP_Enter:
- return TK_PADENTER;
- case XK_F1:
- return TK_F1;
- case XK_F2:
- return TK_F2;
- case XK_F3:
- return TK_F3;
- case XK_F4:
- return TK_F4;
- case XK_F5:
- return TK_F5;
- case XK_F6:
- return TK_F6;
- case XK_F7:
- return TK_F7;
- case XK_F8:
- return TK_F8;
- case XK_F9:
- return TK_F9;
- case XK_F10:
- return TK_F10;
- case XK_F11:
- return TK_F11;
- case XK_F12:
- return TK_F12;
- case XK_BackSpace:
- return TK_BACKSPACE;
- case XK_Tab:
- return TK_TAB;
- case XK_Return:
- return TK_RETURN;
- case XK_Pause:
- return TK_PAUSE;
- case XK_Caps_Lock:
- return TK_CAPSLOCK;
- case XK_Escape:
- return TK_ESCAPE;
- case XK_space:
- return TK_SPACE;
- case XK_Page_Up:
- return TK_PAGEUP;
- case XK_Page_Down:
- return TK_PAGEDN;
- case XK_End:
- return TK_END;
- case XK_Home:
- return TK_HOME;
- case XK_Left:
- return TK_LEFT;
- case XK_Up:
- return TK_UP;
- case XK_Right:
- return TK_RIGHT;
- case XK_Down:
- return TK_DOWN;
- case XK_Insert:
- return TK_INSERT;
- case XK_Delete:
- return TK_DELETE;
- case XK_Meta_L:
- return TK_LWIN;
- case XK_Meta_R:
- return TK_RWIN;
- case XK_Num_Lock:
- return TK_NUMLOCK;
- case XK_Scroll_Lock:
- return TK_SCROLL;
- case XK_Shift_L:
- return TK_LSHIFT;
- case XK_Shift_R:
- return TK_RSHIFT;
- case XK_Control_L:
- return TK_LCONTROL;
- case XK_Control_R:
- return TK_RCONTROL;
- case XK_Alt_L:
- return TK_LALT;
- case XK_Alt_R:
- return TK_RALT;
- case XK_semicolon:
- return TK_SEMICOLON;
- case XK_equal:
- return TK_EQUALS;
- case XK_comma:
- return TK_COMMA;
- case XK_minus:
- return TK_MINUS;
- case XK_period:
- return TK_DOT;
- case XK_slash:
- return TK_SLASH;
- case XK_grave:
- return TK_BACKTICK;
- case XK_bracketleft:
- return TK_LSQUARE;
- case XK_backslash:
- return TK_BACKSLASH;
- case XK_bracketright:
- return TK_RSQUARE;
- case XK_apostrophe:
- return TK_TICK;
- }
- return 0;
- }
- static void tigrUpdateModifiers(TigrInternal* win) {
- win->keys[TK_SHIFT] = win->keys[TK_LSHIFT] || win->keys[TK_RSHIFT];
- win->keys[TK_CONTROL] = win->keys[TK_LCONTROL] || win->keys[TK_RCONTROL];
- win->keys[TK_ALT] = win->keys[TK_LALT] || win->keys[TK_RALT];
- }
- static void tigrInterpretChar(TigrInternal* win, Window root, unsigned int keycode, unsigned int mask) {
- XKeyEvent event;
- memset(&event, 0, sizeof(event));
- event.type = KeyPress;
- event.display = win->dpy;
- event.root = root;
- event.window = win->win;
- event.state = mask;
- event.keycode = keycode;
- char inputTextUTF8[10];
- Status status = 0;
- Xutf8LookupString(win->ic, &event, inputTextUTF8, sizeof(inputTextUTF8), NULL, &status);
- if (status == XLookupChars) {
- tigrDecodeUTF8(inputTextUTF8, &win->lastChar);
- }
- }
- static void tigrProcessInput(TigrInternal* win, int winWidth, int winHeight) {
- {
- Window focused;
- int revertTo;
- XGetInputFocus(win->dpy, &focused, &revertTo);
- if (win->win != focused) {
- return;
- }
- }
- Window root;
- Window child;
- int rootX;
- int rootY;
- int winX;
- int winY;
- unsigned int mask;
- if (XQueryPointer(win->dpy, win->win, &root, &child, &rootX, &rootY, &winX, &winY, &mask)) {
- static unsigned int prevButtons;
- unsigned int buttons = mask & (Button1Mask | Button2Mask | Button3Mask);
- win->mouseX = (winX - win->pos[0]) / win->scale;
- win->mouseY = (winY - win->pos[1]) / win->scale;
- if (buttons != prevButtons && (winX > 0 && winX < winWidth) && (winY > 0 && winY < winHeight)) {
- win->mouseButtons = (buttons & Button1Mask) ? 1
- : 0 | (buttons & Button3Mask) ? 2
- : 0 | (buttons & Button2Mask) ? 4
- : 0;
- }
- prevButtons = buttons;
- }
- // Retrieve mouse wheel events - Cannot be done in the XQueryPointer call
- // -> https://comp.unix.programmer.narkive.com/eOnjkQ6L/xlib-xquerypointer-and-button4mask-button5mask#post2
- // Button4 = WheelUp / Button5 = WheelDown
- XEvent mouseButtonEvent;
- while (XPending(win->dpy)) {
- XPeekEvent(win->dpy, &mouseButtonEvent);
- if (mouseButtonEvent.xany.type == ButtonPress) {
- switch (mouseButtonEvent.xbutton.button) {
- case Button4:
- win->scrollDeltaY += 1;
- break;
- case Button5:
- win->scrollDeltaY -= 1;
- break;
- case 6:
- win->scrollDeltaX += 1;
- break;
- case 7:
- win->scrollDeltaX -= 1;
- break;
- }
- XNextEvent(win->dpy, &mouseButtonEvent);
- } else {
- break;
- }
- }
- static char prevKeys[32];
- char keys[32];
- XQueryKeymap(win->dpy, keys);
- for (int i = 0; i < 32; i++) {
- char thisBlock = keys[i];
- char prevBlock = prevKeys[i];
- if (thisBlock != prevBlock) {
- for (int j = 0; j < 8; j++) {
- int thisBit = thisBlock & 1;
- int prevBit = prevBlock & 1;
- thisBlock >>= 1;
- prevBlock >>= 1;
- if (thisBit != prevBit) {
- int keyCode = 8 * i + j;
- KeySym keySym = XkbKeycodeToKeysym(win->dpy, keyCode, 0, 0);
- if (keySym != NoSymbol) {
- int key = tigrKeyFromX11(keySym);
- win->keys[key] = thisBit;
- tigrUpdateModifiers(win);
- if (thisBit) {
- tigrInterpretChar(win, root, keyCode, mask);
- }
- }
- }
- }
- }
- }
- memcpy(prevKeys, keys, 32);
- XEvent event;
- while (XCheckTypedWindowEvent(win->dpy, win->win, ClientMessage, &event)) {
- if (event.xclient.data.l[0] == wmDeleteMessage) {
- glXMakeCurrent(win->dpy, None, NULL);
- glXDestroyContext(win->dpy, win->glc);
- XDestroyWindow(win->dpy, win->win);
- win->win = 0;
- }
- }
- XFlush(win->dpy);
- }
- void tigrUpdate(Tigr* bmp) {
- XWindowAttributes gwa;
- TigrInternal* win = tigrInternal(bmp);
- memcpy(win->prev, win->keys, 256);
- win->scrollDeltaX = 0;
- win->scrollDeltaY = 0;
- XGetWindowAttributes(win->dpy, win->win, &gwa);
- if (win->flags & TIGR_AUTO)
- tigrResize(bmp, gwa.width / win->scale, gwa.height / win->scale);
- else
- win->scale = tigrEnforceScale(tigrCalcScale(bmp->w, bmp->h, gwa.width, gwa.height), win->flags);
- tigrPosition(bmp, win->scale, gwa.width, gwa.height, win->pos);
- glXMakeCurrent(win->dpy, win->win, win->glc);
- tigrGAPIPresent(bmp, gwa.width, gwa.height);
- glXSwapBuffers(win->dpy, win->win);
- tigrProcessInput(win, gwa.width, gwa.height);
- }
- void tigrFree(Tigr* bmp) {
- if (bmp->handle) {
- TigrInternal* win = tigrInternal(bmp);
- if (win->win) {
- glXMakeCurrent(win->dpy, None, NULL);
- glXDestroyContext(win->dpy, win->glc);
- XDestroyWindow(win->dpy, win->win);
- win->win = 0;
- }
- }
- free(bmp->pix);
- free(bmp);
- }
- void tigrError(Tigr* bmp, const char* message, ...) {
- char tmp[1024];
- va_list args;
- va_start(args, message);
- vsnprintf(tmp, sizeof(tmp), message, args);
- tmp[sizeof(tmp) - 1] = 0;
- va_end(args);
- printf("tigr fatal error: %s\n", tmp);
- exit(1);
- }
- float tigrTime() {
- static double lastTime = 0;
- struct timeval tv;
- gettimeofday(&tv, NULL);
- double now = (double)tv.tv_sec + (tv.tv_usec / 1000000.0);
- double elapsed = lastTime == 0 ? 0 : now - lastTime;
- lastTime = now;
- return (float)elapsed;
- }
- void tigrMouse(Tigr* bmp, int* x, int* y, int* buttons) {
- TigrInternal* win = tigrInternal(bmp);
- if (x) {
- *x = win->mouseX;
- }
- if (y) {
- *y = win->mouseY;
- }
- if (buttons) {
- *buttons = win->mouseButtons;
- }
- }
- int tigrTouch(Tigr* bmp, TigrTouchPoint* points, int maxPoints) {
- int buttons = 0;
- if (maxPoints > 0) {
- tigrMouse(bmp, &points->x, &points->y, &buttons);
- }
- return buttons ? 1 : 0;
- }
- void tigrScrollWheel(Tigr* bmp, float* x, float* y) {
- TigrInternal* win;
- win = tigrInternal(bmp);
- if (x) {
- *x = win->scrollDeltaX;
- }
- if (y) {
- *y = win->scrollDeltaY;
- }
- }
- #endif // __linux__ && !__ANDROID__
- #endif // #ifndef TIGR_HEADLESS
- //////// End of inlined file: tigr_linux.c ////////
- //////// Start of inlined file: tigr_android.c ////////
- #ifndef TIGR_HEADLESS
- //#include "tigr_internal.h"
- #ifdef __ANDROID__
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #include <time.h>
- #include <android/log.h>
- #include <android/native_window.h>
- #include <android/input.h>
- #ifndef NDEBUG
- #define LOGD(...) ((void)__android_log_print(ANDROID_LOG_DEBUG, "tigr", __VA_ARGS__))
- #else
- #define LOGD(...) ((void)0)
- #endif
- #define LOGI(...) ((void)__android_log_print(ANDROID_LOG_INFO, "tigr", __VA_ARGS__))
- #define LOGE(...) ((void)__android_log_print(ANDROID_LOG_ERROR, "tigr", __VA_ARGS__))
- typedef struct {
- TigrTouchPoint points[MAX_TOUCH_POINTS];
- int numPoints;
- } InputState;
- /// Global state
- static struct {
- ANativeWindow* window;
- InputState inputState;
- EGLDisplay display;
- EGLSurface surface;
- EGLint screenW;
- EGLint screenH;
- EGLConfig config;
- double lastTime;
- int closed;
- } gState = {
- .window = 0,
- .inputState = {
- .numPoints = 0,
- },
- .display = EGL_NO_DISPLAY,
- .surface = EGL_NO_SURFACE,
- .screenW = 0,
- .screenH = 0,
- .config = 0,
- .lastTime = 0,
- .closed = 0,
- };
- static const EGLint contextAttribs[] = { EGL_CONTEXT_MAJOR_VERSION, 3, EGL_CONTEXT_MINOR_VERSION, 0, EGL_NONE };
- static EGLConfig getGLConfig(EGLDisplay display) {
- EGLConfig config = 0;
- const EGLint attribs[] = { EGL_SURFACE_TYPE, EGL_WINDOW_BIT, EGL_BLUE_SIZE, 8, EGL_GREEN_SIZE, 8, EGL_RED_SIZE, 8,
- EGL_NONE };
- EGLint numConfigs;
- eglChooseConfig(display, attribs, NULL, 0, &numConfigs);
- EGLConfig* supportedConfigs = (EGLConfig*)malloc(sizeof(EGLConfig) * numConfigs);
- eglChooseConfig(display, attribs, supportedConfigs, numConfigs, &numConfigs);
- int i = 0;
- for (; i < numConfigs; i++) {
- EGLConfig* cfg = supportedConfigs[i];
- EGLint r, g, b, d;
- if (eglGetConfigAttrib(display, cfg, EGL_RED_SIZE, &r) &&
- eglGetConfigAttrib(display, cfg, EGL_GREEN_SIZE, &g) &&
- eglGetConfigAttrib(display, cfg, EGL_BLUE_SIZE, &b) &&
- eglGetConfigAttrib(display, cfg, EGL_DEPTH_SIZE, &d) && r == 8 && g == 8 && b == 8 && d == 0) {
- config = supportedConfigs[i];
- break;
- }
- }
- if (i == numConfigs) {
- config = supportedConfigs[0];
- }
- if (config == NULL) {
- tigrError(NULL, "Unable to initialize EGLConfig");
- }
- free(supportedConfigs);
- return config;
- }
- /// Android interface, called from main thread
- void tigr_android_create() {
- gState.closed = 0;
- gState.window = 0;
- gState.inputState.numPoints = 0;
- gState.surface = EGL_NO_SURFACE;
- gState.lastTime = 0;
- if (gState.display == EGL_NO_DISPLAY) {
- gState.display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
- EGLBoolean status = eglInitialize(gState.display, NULL, NULL);
- if (!status) {
- tigrError(NULL, "Failed to init EGL");
- }
- gState.config = getGLConfig(gState.display);
- }
- }
- void tigr_android_destroy() {
- eglTerminate(gState.display);
- gState.display = EGL_NO_DISPLAY;
- }
- /// Internals ///
- static void logEglError() {
- int error = eglGetError();
- switch (error) {
- case EGL_BAD_DISPLAY:
- LOGE("EGL error: Bad display");
- break;
- case EGL_NOT_INITIALIZED:
- LOGE("EGL error: Not initialized");
- break;
- case EGL_BAD_NATIVE_WINDOW:
- LOGE("EGL error: Bad native window");
- break;
- case EGL_BAD_ALLOC:
- LOGE("EGL error: Bad alloc");
- break;
- case EGL_BAD_MATCH:
- LOGE("EGL error: Bad match");
- break;
- default:
- LOGE("EGL error: %d", error);
- }
- }
- static void setupOpenGL() {
- LOGD("setupOpenGL");
- assert(gState.surface == EGL_NO_SURFACE);
- assert(gState.window != 0);
- gState.surface = eglCreateWindowSurface(gState.display, gState.config, gState.window, NULL);
- if (gState.surface == EGL_NO_SURFACE) {
- logEglError();
- }
- assert(gState.surface != EGL_NO_SURFACE);
- eglQuerySurface(gState.display, gState.surface, EGL_WIDTH, &gState.screenW);
- eglQuerySurface(gState.display, gState.surface, EGL_HEIGHT, &gState.screenH);
- LOGD("Screen is %d x %d", gState.screenW, gState.screenH);
- }
- static void tearDownOpenGL() {
- LOGD("tearDownOpenGL");
- if (gState.display != EGL_NO_DISPLAY) {
- eglMakeCurrent(gState.display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
- if (gState.surface != EGL_NO_SURFACE) {
- LOGD("eglDestroySurface");
- if (!eglDestroySurface(gState.display, gState.surface)) {
- logEglError();
- }
- gState.surface = EGL_NO_SURFACE;
- }
- }
- }
- static int tigrKeyFromAndroidKey(int key) {
- switch (key) {
- case AKEYCODE_Q:
- return 'Q';
- case AKEYCODE_W:
- return 'W';
- case AKEYCODE_E:
- return 'E';
- case AKEYCODE_R:
- return 'R';
- case AKEYCODE_T:
- return 'T';
- case AKEYCODE_Y:
- return 'Y';
- case AKEYCODE_U:
- return 'U';
- case AKEYCODE_I:
- return 'I';
- case AKEYCODE_O:
- return 'O';
- case AKEYCODE_P:
- return 'P';
- case AKEYCODE_A:
- return 'A';
- case AKEYCODE_S:
- return 'S';
- case AKEYCODE_D:
- return 'D';
- case AKEYCODE_F:
- return 'F';
- case AKEYCODE_G:
- return 'G';
- case AKEYCODE_H:
- return 'H';
- case AKEYCODE_J:
- return 'J';
- case AKEYCODE_K:
- return 'K';
- case AKEYCODE_L:
- return 'L';
- case AKEYCODE_Z:
- return 'Z';
- case AKEYCODE_X:
- return 'X';
- case AKEYCODE_C:
- return 'C';
- case AKEYCODE_V:
- return 'V';
- case AKEYCODE_B:
- return 'B';
- case AKEYCODE_N:
- return 'N';
- case AKEYCODE_M:
- return 'M';
- case AKEYCODE_0:
- return '0';
- case AKEYCODE_1:
- return '1';
- case AKEYCODE_2:
- return '2';
- case AKEYCODE_3:
- return '3';
- case AKEYCODE_4:
- return '4';
- case AKEYCODE_5:
- return '5';
- case AKEYCODE_6:
- return '6';
- case AKEYCODE_7:
- return '7';
- case AKEYCODE_8:
- return '8';
- case AKEYCODE_9:
- return '9';
- case AKEYCODE_NUMPAD_0:
- return TK_PAD0;
- case AKEYCODE_NUMPAD_1:
- return TK_PAD1;
- case AKEYCODE_NUMPAD_2:
- return TK_PAD2;
- case AKEYCODE_NUMPAD_3:
- return TK_PAD3;
- case AKEYCODE_NUMPAD_4:
- return TK_PAD4;
- case AKEYCODE_NUMPAD_5:
- return TK_PAD5;
- case AKEYCODE_NUMPAD_6:
- return TK_PAD6;
- case AKEYCODE_NUMPAD_7:
- return TK_PAD7;
- case AKEYCODE_NUMPAD_8:
- return TK_PAD8;
- case AKEYCODE_NUMPAD_9:
- return TK_PAD9;
- case AKEYCODE_NUMPAD_MULTIPLY:
- return TK_PADMUL;
- case AKEYCODE_NUMPAD_DIVIDE:
- return TK_PADDIV;
- case AKEYCODE_NUMPAD_ADD:
- return TK_PADADD;
- case AKEYCODE_NUMPAD_SUBTRACT:
- return TK_PADSUB;
- case AKEYCODE_NUMPAD_ENTER:
- return TK_PADENTER;
- case AKEYCODE_NUMPAD_DOT:
- return TK_PADDOT;
- case AKEYCODE_F1:
- return TK_F1;
- case AKEYCODE_F2:
- return TK_F2;
- case AKEYCODE_F3:
- return TK_F3;
- case AKEYCODE_F4:
- return TK_F4;
- case AKEYCODE_F5:
- return TK_F5;
- case AKEYCODE_F6:
- return TK_F6;
- case AKEYCODE_F7:
- return TK_F7;
- case AKEYCODE_F8:
- return TK_F8;
- case AKEYCODE_F9:
- return TK_F9;
- case AKEYCODE_F10:
- return TK_F10;
- case AKEYCODE_F11:
- return TK_F11;
- case AKEYCODE_F12:
- return TK_F12;
- case AKEYCODE_SHIFT_LEFT:
- return TK_LSHIFT;
- case AKEYCODE_SHIFT_RIGHT:
- return TK_RSHIFT;
- case AKEYCODE_CTRL_LEFT:
- return TK_LCONTROL;
- case AKEYCODE_CTRL_RIGHT:
- return TK_RCONTROL;
- case AKEYCODE_ALT_LEFT:
- return TK_LALT;
- case AKEYCODE_ALT_RIGHT:
- return TK_RALT;
- case AKEYCODE_META_LEFT:
- return TK_LWIN;
- case AKEYCODE_META_RIGHT:
- return TK_RWIN;
- case AKEYCODE_DEL:
- return TK_BACKSPACE;
- case AKEYCODE_TAB:
- return TK_TAB;
- case AKEYCODE_ENTER:
- return TK_RETURN;
- case AKEYCODE_CAPS_LOCK:
- return TK_CAPSLOCK;
- case AKEYCODE_ESCAPE:
- case AKEYCODE_BACK:
- return TK_ESCAPE;
- case AKEYCODE_SPACE:
- return TK_SPACE;
- case AKEYCODE_PAGE_UP:
- return TK_PAGEUP;
- case AKEYCODE_PAGE_DOWN:
- return TK_PAGEDN;
- case AKEYCODE_MOVE_END:
- return TK_END;
- case AKEYCODE_MOVE_HOME:
- return TK_HOME;
- case AKEYCODE_DPAD_LEFT:
- return TK_LEFT;
- case AKEYCODE_DPAD_RIGHT:
- return TK_RIGHT;
- case AKEYCODE_DPAD_UP:
- return TK_UP;
- case AKEYCODE_DPAD_DOWN:
- return TK_DOWN;
- case AKEYCODE_INSERT:
- return TK_INSERT;
- case AKEYCODE_FORWARD_DEL:
- return TK_DELETE;
- case AKEYCODE_NUM_LOCK:
- return TK_NUMLOCK;
- case AKEYCODE_SCROLL_LOCK:
- return TK_SCROLL;
- case AKEYCODE_SEMICOLON:
- return TK_SEMICOLON;
- case AKEYCODE_EQUALS:
- return TK_EQUALS;
- case AKEYCODE_COMMA:
- return TK_COMMA;
- case AKEYCODE_MINUS:
- return TK_MINUS;
- case AKEYCODE_PERIOD:
- return TK_DOT;
- case AKEYCODE_SLASH:
- return TK_SLASH;
- case AKEYCODE_BACKSLASH:
- return TK_BACKSLASH;
- case AKEYCODE_GRAVE:
- return TK_BACKTICK;
- case AKEYCODE_APOSTROPHE:
- return TK_TICK;
- case AKEYCODE_LEFT_BRACKET:
- return TK_LSQUARE;
- case AKEYCODE_RIGHT_BRACKET:
- return TK_RSQUARE;
- default:
- return 0;
- }
- }
- static int processInputEvent(AInputEvent* event, TigrInternal* win) {
- if (AInputEvent_getType(event) == AINPUT_EVENT_TYPE_MOTION) {
- int32_t action = AMotionEvent_getAction(event);
- int32_t actionCode = action & AMOTION_EVENT_ACTION_MASK;
- size_t touchPoints = AMotionEvent_getPointerCount(event);
- size_t releasedIndex = -1;
- if (actionCode == AMOTION_EVENT_ACTION_POINTER_UP) {
- releasedIndex =
- (action & AMOTION_EVENT_ACTION_POINTER_INDEX_MASK) >> AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT;
- }
- size_t targetPointCount = 0;
- for (size_t i = 0; i < touchPoints && i < MAX_TOUCH_POINTS; i++) {
- if (i == releasedIndex) {
- continue;
- }
- gState.inputState.points[targetPointCount].x = AMotionEvent_getX(event, i);
- gState.inputState.points[targetPointCount].y = AMotionEvent_getY(event, i);
- targetPointCount++;
- }
- if (actionCode == AMOTION_EVENT_ACTION_UP || actionCode == AMOTION_EVENT_ACTION_CANCEL) {
- gState.inputState.numPoints = 0;
- } else {
- gState.inputState.numPoints = targetPointCount;
- }
- return 1;
- } else if (AInputEvent_getType(event) == AINPUT_EVENT_TYPE_KEY) {
- if (!win) {
- return 1;
- }
- int32_t deviceID = AInputEvent_getDeviceId(event);
- // KeyCharacterMap#VIRTUAL_KEYBOARD == -1
- if (deviceID != -1) {
- return 1;
- }
- int32_t action = AKeyEvent_getAction(event);
- int32_t keyCode = AKeyEvent_getKeyCode(event);
- int key = tigrKeyFromAndroidKey(keyCode);
- // We pass the character in the scancode field from the Java side
- int32_t unicodeChar = AKeyEvent_getScanCode(event);
- if (action == AKEY_EVENT_ACTION_DOWN) {
- win->keys[key] = 1;
- win->lastChar = unicodeChar;
- } else if (action == AKEY_EVENT_ACTION_UP) {
- win->released[key] = 1;
- }
- return 1;
- }
- return 0;
- }
- static int handleEvent(AndroidEvent event, void* userData) {
- switch (event.type) {
- case AE_WINDOW_CREATED:
- gState.window = event.window;
- setupOpenGL();
- break;
- case AE_WINDOW_DESTROYED:
- tearDownOpenGL();
- gState.window = 0;
- break;
- case AE_INPUT:
- return processInputEvent(event.inputEvent, (TigrInternal*)userData);
- case AE_RESUME:
- gState.lastTime = event.time;
- break;
- case AE_CLOSE:
- gState.closed = 1;
- break;
- default:
- LOGE("Unhandled event type: %d", event.type);
- return 0;
- }
- return 1;
- }
- static int processEvents(TigrInternal* win) {
- if (gState.closed) {
- return 0;
- }
- while (android_pollEvent(handleEvent, win)) {
- if (gState.closed) {
- return 0;
- }
- }
- return 1;
- }
- static Tigr* refreshWindow(Tigr* bmp) {
- if (bmp->handle == gState.window) {
- return bmp;
- }
- bmp->handle = gState.window;
- if (gState.window == 0) {
- return 0;
- }
- TigrInternal* win = tigrInternal(bmp);
- int scale = 1;
- if (win->flags & TIGR_AUTO) {
- // Always use a 1:1 pixel size.
- scale = 1;
- } else {
- // See how big we can make it and still fit on-screen.
- scale = tigrCalcScale(bmp->w, bmp->h, gState.screenW, gState.screenH);
- }
- win->scale = tigrEnforceScale(scale, win->flags);
- return bmp;
- }
- /// TIGR interface implementation, called from render thread ///
- Tigr* tigrWindow(int w, int h, const char* title, int flags) {
- while (gState.window == NULL) {
- if (!processEvents(0)) {
- return NULL;
- }
- }
- EGLContext context = eglCreateContext(gState.display, gState.config, NULL, contextAttribs);
- int scale = 1;
- if (flags & TIGR_AUTO) {
- // Always use a 1:1 pixel size.
- scale = 1;
- } else {
- // See how big we can make it and still fit on-screen.
- scale = tigrCalcScale(w, h, gState.screenW, gState.screenH);
- }
- scale = tigrEnforceScale(scale, flags);
- Tigr* bmp = tigrBitmap2(w, h, sizeof(TigrInternal));
- bmp->handle = (void*)gState.window;
- TigrInternal* win = tigrInternal(bmp);
- win->context = context;
- win->shown = 0;
- win->closed = 0;
- win->scale = scale;
- win->lastChar = 0;
- win->flags = flags;
- win->p1 = win->p2 = win->p3 = 0;
- win->p4 = 1;
- win->widgetsWanted = 0;
- win->widgetAlpha = 0;
- win->widgetsScale = 0;
- win->widgets = 0;
- win->gl.gl_legacy = 0;
- memset(win->keys, 0, 256);
- memset(win->prev, 0, 256);
- memset(win->released, 0, 256);
- tigrPosition(bmp, win->scale, bmp->w, bmp->h, win->pos);
- if (eglMakeCurrent(gState.display, gState.surface, gState.surface, context) == EGL_FALSE) {
- LOGE("Unable to eglMakeCurrent");
- return 0;
- }
- tigrGAPICreate(bmp);
- return bmp;
- }
- int tigrClosed(Tigr* bmp) {
- TigrInternal* win = tigrInternal(bmp);
- return win->closed;
- }
- int tigrGAPIBegin(Tigr* bmp) {
- assert(gState.display != EGL_NO_DISPLAY);
- assert(gState.surface != EGL_NO_SURFACE);
- TigrInternal* win = tigrInternal(bmp);
- if (eglMakeCurrent(gState.display, gState.surface, gState.surface, win->context) == EGL_FALSE) {
- return -1;
- }
- return 0;
- }
- int tigrGAPIEnd(Tigr* bmp) {
- (void)bmp;
- eglMakeCurrent(gState.display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
- return 0;
- }
- int tigrKeyDown(Tigr* bmp, int key) {
- TigrInternal* win;
- assert(key < 256);
- win = tigrInternal(bmp);
- return win->keys[key] && !win->prev[key];
- }
- int tigrKeyHeld(Tigr* bmp, int key) {
- TigrInternal* win;
- assert(key < 256);
- win = tigrInternal(bmp);
- return win->keys[key];
- }
- int tigrReadChar(Tigr* bmp) {
- TigrInternal* win = tigrInternal(bmp);
- int c = win->lastChar;
- win->lastChar = 0;
- return c;
- }
- static void tigrUpdateModifiers(TigrInternal* win) {
- win->keys[TK_SHIFT] = win->keys[TK_LSHIFT] || win->keys[TK_RSHIFT];
- win->keys[TK_CONTROL] = win->keys[TK_LCONTROL] || win->keys[TK_RCONTROL];
- win->keys[TK_ALT] = win->keys[TK_LALT] || win->keys[TK_RALT];
- }
- static int toWindowX(TigrInternal* win, int x) {
- return (x - win->pos[0]) / win->scale;
- }
- static int toWindowY(TigrInternal* win, int y) {
- return (y - win->pos[1]) / win->scale;
- }
- void tigrUpdate(Tigr* bmp) {
- TigrInternal* win = tigrInternal(bmp);
- memcpy(win->prev, win->keys, 256);
- for (int i = 0; i < 256; i++) {
- win->keys[i] ^= win->released[i];
- win->released[i] = 0;
- }
- if (!processEvents(win)) {
- win->closed = 1;
- return;
- }
- tigrUpdateModifiers(win);
- if (gState.window == 0) {
- return;
- }
- bmp = refreshWindow(bmp);
- if (bmp == 0) {
- return;
- }
- win->numTouchPoints = gState.inputState.numPoints;
- for (int i = 0; i < win->numTouchPoints; i++) {
- win->touchPoints[i].x = toWindowX(win, gState.inputState.points[i].x);
- win->touchPoints[i].y = toWindowY(win, gState.inputState.points[i].y);
- }
- win->mouseButtons = win->numTouchPoints;
- if (win->mouseButtons > 0) {
- win->mouseX = win->touchPoints[0].x;
- win->mouseY = win->touchPoints[0].y;
- }
- if (win->flags & TIGR_AUTO) {
- tigrResize(bmp, gState.screenW / win->scale, gState.screenH / win->scale);
- } else {
- win->scale = tigrEnforceScale(tigrCalcScale(bmp->w, bmp->h, gState.screenW, gState.screenH), win->flags);
- }
- tigrPosition(bmp, win->scale, gState.screenW, gState.screenH, win->pos);
- tigrGAPIBegin(bmp);
- tigrGAPIPresent(bmp, gState.screenW, gState.screenH);
- android_swap(gState.display, gState.surface);
- tigrGAPIEnd(bmp);
- }
- void tigrFree(Tigr* bmp) {
- if (bmp->handle) {
- TigrInternal* win = tigrInternal(bmp);
- eglMakeCurrent(gState.display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
- if (win->context != EGL_NO_CONTEXT) {
- // Win closed means app windows has closed, and the call would fail.
- if (!win->closed) {
- tigrGAPIDestroy(bmp);
- }
- eglDestroyContext(gState.display, win->context);
- }
- win->context = EGL_NO_CONTEXT;
- }
- free(bmp->pix);
- free(bmp);
- }
- void tigrError(Tigr* bmp, const char* message, ...) {
- char tmp[1024];
- va_list args;
- va_start(args, message);
- vsnprintf(tmp, sizeof(tmp), message, args);
- tmp[sizeof(tmp) - 1] = 0;
- va_end(args);
- LOGE("tigr fatal error: %s\n", tmp);
- exit(1);
- }
- float tigrTime() {
- struct timespec ts;
- clock_gettime(CLOCK_MONOTONIC, &ts);
- double now = (double)ts.tv_sec + (ts.tv_nsec / 1000000000.0);
- double elapsed = gState.lastTime == 0 ? 0 : now - gState.lastTime;
- gState.lastTime = now;
- return (float)elapsed;
- }
- void tigrMouse(Tigr* bmp, int* x, int* y, int* buttons) {
- TigrInternal* win = tigrInternal(bmp);
- if (x) {
- *x = win->mouseX;
- }
- if (y) {
- *y = win->mouseY;
- }
- if (buttons) {
- *buttons = win->mouseButtons;
- }
- }
- int tigrTouch(Tigr* bmp, TigrTouchPoint* points, int maxPoints) {
- TigrInternal* win = tigrInternal(bmp);
- for (int i = 0; i < maxPoints && i < win->numTouchPoints; i++) {
- points[i] = win->touchPoints[i];
- }
- return maxPoints < win->numTouchPoints ? maxPoints : win->numTouchPoints;
- }
- void tigrMouseWheel(Tigr* bmp, float* x, float* y) {
- if (x) {
- *x = 0;
- }
- if (y) {
- *y = 0;
- }
- }
- void* tigrReadFile(const char* fileName, int* length) {
- if (length != 0) {
- *length = 0;
- }
- void* asset = android_loadAsset(fileName, length);
- return asset;
- }
- #endif // __ANDROID__
- #endif // #ifndef TIGR_HEADLESS
- //////// End of inlined file: tigr_android.c ////////
- //////// Start of inlined file: tigr_gl.c ////////
- #ifndef TIGR_HEADLESS
- //#include "tigr_internal.h"
- #include <assert.h>
- #ifdef TIGR_GAPI_GL
- #if __linux__
- #if __ANDROID__
- #include <GLES3/gl3.h>
- #else
- #define GLX_GLXEXT_PROTOTYPES
- #include <GL/glext.h>
- #endif
- #endif
- extern const char tigr_upscale_gl_vs[], tigr_upscale_gl_fs[], tigr_default_fx_gl_fs[];
- extern const int tigr_upscale_gl_vs_size, tigr_upscale_gl_fs_size, tigr_default_fx_gl_fs_size;
- #ifdef _WIN32
- #ifdef TIGR_GAPI_GL_WIN_USE_GLEXT
- #include <glext.h>
- #include <wglext.h>
- #else // short version of glext.h and wglext.h so we don't need to depend on them
- #ifndef APIENTRY
- #define APIENTRY
- #endif
- #ifndef APIENTRYP
- #define APIENTRYP APIENTRY*
- #endif
- typedef ptrdiff_t GLsizeiptr;
- #define GL_COMPILE_STATUS 0x8B81
- #define GL_LINK_STATUS 0x8B82
- #define GL_ARRAY_BUFFER 0x8892
- #define GL_STATIC_DRAW 0x88E4
- #define GL_VERTEX_SHADER 0x8B31
- #define GL_FRAGMENT_SHADER 0x8B30
- #define GL_BGRA 0x80E1
- #define GL_TEXTURE0 0x84C0
- typedef void(APIENTRYP PFNGLGENVERTEXARRAYSPROC)(GLsizei n, GLuint* arrays);
- typedef void(APIENTRYP PFNGLGENBUFFERSARBPROC)(GLsizei n, GLuint* buffers);
- typedef void(APIENTRYP PFNGLBINDBUFFERPROC)(GLenum target, GLuint buffer);
- typedef void(APIENTRYP PFNGLBUFFERDATAPROC)(GLenum target, GLsizeiptr size, const void* data, GLenum usage);
- typedef void(APIENTRYP PFNGLBINDVERTEXARRAYPROC)(GLuint array);
- typedef void(APIENTRYP PFNGLENABLEVERTEXATTRIBARRAYPROC)(GLuint index);
- typedef void(APIENTRYP PFNGLVERTEXATTRIBPOINTERPROC)(GLuint index,
- GLint size,
- GLenum type,
- GLboolean normalized,
- GLsizei stride,
- const void* pointer);
- typedef GLuint(APIENTRYP PFNGLCREATESHADERPROC)(GLenum type);
- typedef char GLchar;
- typedef void(APIENTRYP PFNGLSHADERSOURCEPROC)(GLuint shader,
- GLsizei count,
- const GLchar* const* string,
- const GLint* length);
- typedef void(APIENTRYP PFNGLCOMPILESHADERPROC)(GLuint shader);
- typedef GLuint(APIENTRYP PFNGLCREATEPROGRAMPROC)(void);
- typedef void(APIENTRYP PFNGLATTACHSHADERPROC)(GLuint program, GLuint shader);
- typedef void(APIENTRYP PFNGLLINKPROGRAMPROC)(GLuint program);
- typedef void(APIENTRYP PFNGLDELETESHADERPROC)(GLuint shader);
- typedef void(APIENTRYP PFNGLDELETEPROGRAMPROC)(GLuint program);
- typedef void(APIENTRYP PFNGLGETSHADERIVPROC)(GLuint shader, GLenum pname, GLint* params);
- typedef void(APIENTRYP PFNGLGETSHADERINFOLOGPROC)(GLuint shader, GLsizei bufSize, GLsizei* length, GLchar* infoLog);
- typedef void(APIENTRYP PFNGLGETPROGRAMIVPROC)(GLuint program, GLenum pname, GLint* params);
- typedef void(APIENTRYP PFNGLGETPROGRAMINFOLOGPROC)(GLuint program, GLsizei bufSize, GLsizei* length, GLchar* infoLog);
- typedef void(APIENTRYP PFNGLUSEPROGRAMPROC)(GLuint program);
- typedef GLint(APIENTRYP PFNGLGETUNIFORMLOCATIONPROC)(GLuint program, const GLchar* name);
- typedef void(APIENTRYP PFNGLUNIFORM4FPROC)(GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3);
- typedef void(APIENTRYP PFNGLUNIFORMMATRIX4FVPROC)(GLint location,
- GLsizei count,
- GLboolean transpose,
- const GLfloat* value);
- typedef void(APIENTRYP PFNGLACTIVETEXTUREPROC)(GLenum texture);
- #define WGL_DRAW_TO_WINDOW_ARB 0x2001
- #define WGL_SUPPORT_OPENGL_ARB 0x2010
- #define WGL_DOUBLE_BUFFER_ARB 0x2011
- #define WGL_PIXEL_TYPE_ARB 0x2013
- #define WGL_COLOR_BITS_ARB 0x2014
- #define WGL_DEPTH_BITS_ARB 0x2022
- #define WGL_STENCIL_BITS_ARB 0x2023
- #define WGL_TYPE_RGBA_ARB 0x202B
- #define WGL_CONTEXT_MAJOR_VERSION_ARB 0x2091
- #define WGL_CONTEXT_MINOR_VERSION_ARB 0x2092
- typedef BOOL(WINAPI* PFNWGLCHOOSEPIXELFORMATARBPROC)(HDC hdc,
- const int* piAttribIList,
- const FLOAT* pfAttribFList,
- UINT nMaxFormats,
- int* piFormats,
- UINT* nNumFormats);
- typedef HGLRC(WINAPI* PFNWGLCREATECONTEXTATTRIBSARBPROC)(HDC hDC, HGLRC hShareContext, const int* attribList);
- #endif
- PFNWGLCHOOSEPIXELFORMATARBPROC wglChoosePixelFormat;
- PFNWGLCREATECONTEXTATTRIBSARBPROC wglCreateContextAttribs;
- PFNGLGENVERTEXARRAYSPROC glGenVertexArrays;
- PFNGLGENBUFFERSARBPROC glGenBuffers;
- PFNGLBINDBUFFERPROC glBindBuffer;
- PFNGLBUFFERDATAPROC glBufferData;
- PFNGLBINDVERTEXARRAYPROC glBindVertexArray;
- PFNGLENABLEVERTEXATTRIBARRAYPROC glEnableVertexAttribArray;
- PFNGLVERTEXATTRIBPOINTERPROC glVertexAttribPointer;
- PFNGLCREATESHADERPROC glCreateShader;
- PFNGLSHADERSOURCEPROC glShaderSource;
- PFNGLCOMPILESHADERPROC glCompileShader;
- PFNGLCREATEPROGRAMPROC glCreateProgram;
- PFNGLATTACHSHADERPROC glAttachShader;
- PFNGLLINKPROGRAMPROC glLinkProgram;
- PFNGLDELETESHADERPROC glDeleteShader;
- PFNGLDELETEPROGRAMPROC glDeleteProgram;
- PFNGLGETSHADERIVPROC glGetShaderiv;
- PFNGLGETSHADERINFOLOGPROC glGetShaderInfoLog;
- PFNGLGETPROGRAMIVPROC glGetProgramiv;
- PFNGLGETPROGRAMINFOLOGPROC glGetProgramInfoLog;
- PFNGLUSEPROGRAMPROC glUseProgram;
- PFNGLGETUNIFORMLOCATIONPROC glGetUniformLocation;
- PFNGLUNIFORM4FPROC glUniform4f;
- PFNGLUNIFORMMATRIX4FVPROC glUniformMatrix4fv;
- PFNGLACTIVETEXTUREPROC glActiveTexture;
- int tigrGL11Init(Tigr* bmp) {
- int pixel_format;
- TigrInternal* win = tigrInternal(bmp);
- GLStuff* gl = &win->gl;
- PIXELFORMATDESCRIPTOR pfd = { sizeof(PIXELFORMATDESCRIPTOR),
- 1,
- PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER | PFD_SWAP_EXCHANGE,
- PFD_TYPE_RGBA,
- 32, // color bits
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 24, // depth
- 8, // stencil
- 0,
- PFD_MAIN_PLANE, // is it ignored ?
- 0,
- 0,
- 0,
- 0 };
- if (!(gl->dc = GetDC((HWND)bmp->handle))) {
- tigrError(bmp, "Cannot create OpenGL device context.\n");
- return -1;
- }
- if (!(pixel_format = ChoosePixelFormat(gl->dc, &pfd))) {
- tigrError(bmp, "Cannot choose OpenGL pixel format.\n");
- return -1;
- }
- if (!SetPixelFormat(gl->dc, pixel_format, &pfd)) {
- tigrError(bmp, "Cannot set OpenGL pixel format.\n");
- return -1;
- }
- if (!(gl->hglrc = wglCreateContext(gl->dc))) {
- tigrError(bmp, "Cannot create OpenGL context.\n");
- return -1;
- }
- if (!wglMakeCurrent(gl->dc, gl->hglrc)) {
- tigrError(bmp, "Cannot activate OpenGL context.\n");
- return -1;
- }
- gl->gl_legacy = 1;
- return 0;
- }
- int tigrGL33Init(Tigr* bmp) {
- int pixel_format;
- UINT num_formats;
- TigrInternal* win = tigrInternal(bmp);
- GLStuff* gl = &win->gl;
- wglChoosePixelFormat = (PFNWGLCHOOSEPIXELFORMATARBPROC)wglGetProcAddress("wglChoosePixelFormatARB");
- wglCreateContextAttribs = (PFNWGLCREATECONTEXTATTRIBSARBPROC)wglGetProcAddress("wglCreateContextAttribsARB");
- glGenVertexArrays = (PFNGLGENVERTEXARRAYSPROC)wglGetProcAddress("glGenVertexArrays");
- glGenBuffers = (PFNGLGENBUFFERSARBPROC)wglGetProcAddress("glGenBuffers");
- glBindBuffer = (PFNGLBINDBUFFERPROC)wglGetProcAddress("glBindBuffer");
- glBufferData = (PFNGLBUFFERDATAPROC)wglGetProcAddress("glBufferData");
- glBindVertexArray = (PFNGLBINDVERTEXARRAYPROC)wglGetProcAddress("glBindVertexArray");
- glEnableVertexAttribArray = (PFNGLENABLEVERTEXATTRIBARRAYPROC)wglGetProcAddress("glEnableVertexAttribArray");
- glVertexAttribPointer = (PFNGLVERTEXATTRIBPOINTERPROC)wglGetProcAddress("glVertexAttribPointer");
- glCreateShader = (PFNGLCREATESHADERPROC)wglGetProcAddress("glCreateShader");
- glShaderSource = (PFNGLSHADERSOURCEPROC)wglGetProcAddress("glShaderSource");
- glCompileShader = (PFNGLCOMPILESHADERPROC)wglGetProcAddress("glCompileShader");
- glCreateProgram = (PFNGLCREATEPROGRAMPROC)wglGetProcAddress("glCreateProgram");
- glAttachShader = (PFNGLATTACHSHADERPROC)wglGetProcAddress("glAttachShader");
- glLinkProgram = (PFNGLLINKPROGRAMPROC)wglGetProcAddress("glLinkProgram");
- glDeleteShader = (PFNGLDELETESHADERPROC)wglGetProcAddress("glDeleteShader");
- glDeleteProgram = (PFNGLDELETEPROGRAMPROC)wglGetProcAddress("glDeleteProgram");
- glGetShaderiv = (PFNGLGETSHADERIVPROC)wglGetProcAddress("glGetShaderiv");
- glGetShaderInfoLog = (PFNGLGETSHADERINFOLOGPROC)wglGetProcAddress("glGetShaderInfoLog");
- glGetProgramiv = (PFNGLGETPROGRAMIVPROC)wglGetProcAddress("glGetProgramiv");
- glGetProgramInfoLog = (PFNGLGETPROGRAMINFOLOGPROC)wglGetProcAddress("glGetProgramInfoLog");
- glUseProgram = (PFNGLUSEPROGRAMPROC)wglGetProcAddress("glUseProgram");
- glGetUniformLocation = (PFNGLGETUNIFORMLOCATIONPROC)wglGetProcAddress("glGetUniformLocation");
- glUniform4f = (PFNGLUNIFORM4FPROC)wglGetProcAddress("glUniform4f");
- glUniformMatrix4fv = (PFNGLUNIFORMMATRIX4FVPROC)wglGetProcAddress("glUniformMatrix4fv");
- glActiveTexture = (PFNGLACTIVETEXTUREPROC)wglGetProcAddress("glActiveTexture");
- if (!wglChoosePixelFormat || !wglCreateContextAttribs) {
- tigrError(bmp, "Cannot create OpenGL context.\n");
- return -1;
- }
- const int attribList[] = { WGL_DRAW_TO_WINDOW_ARB,
- GL_TRUE,
- WGL_SUPPORT_OPENGL_ARB,
- GL_TRUE,
- WGL_DOUBLE_BUFFER_ARB,
- GL_TRUE,
- WGL_PIXEL_TYPE_ARB,
- WGL_TYPE_RGBA_ARB,
- WGL_COLOR_BITS_ARB,
- 32,
- WGL_DEPTH_BITS_ARB,
- 24,
- WGL_STENCIL_BITS_ARB,
- 8,
- 0 };
- int attribs[] = { WGL_CONTEXT_MAJOR_VERSION_ARB, 3, WGL_CONTEXT_MINOR_VERSION_ARB, 3, 0 };
- if (!wglChoosePixelFormat(gl->dc, attribList, NULL, 1, &pixel_format, &num_formats)) {
- tigrError(bmp, "Cannot choose OpenGL pixel format.\n");
- return -1;
- }
- if (!(gl->hglrc = wglCreateContextAttribs(gl->dc, gl->hglrc, attribs))) {
- tigrError(bmp, "Cannot create OpenGL context attribs.\n");
- return -1;
- }
- if (!wglMakeCurrent(gl->dc, gl->hglrc)) {
- tigrError(bmp, "Cannot activate OpenGL context.\n");
- return -1;
- }
- gl->gl_legacy = 0;
- return 0;
- }
- #endif
- void tigrCheckGLError(const char* state) {
- GLenum err = glGetError();
- if (err != GL_NO_ERROR) {
- tigrError(NULL, "got GL error %x when doing %s\n", err, state);
- }
- }
- void tigrCheckShaderErrors(GLuint object) {
- GLint success;
- GLchar info[2048];
- glGetShaderiv(object, GL_COMPILE_STATUS, &success);
- if (!success) {
- glGetShaderInfoLog(object, sizeof(info), NULL, info);
- tigrError(NULL, "shader compile error : %s\n", info);
- }
- }
- void tigrCheckProgramErrors(GLuint object) {
- GLint success;
- GLchar info[2048];
- glGetProgramiv(object, GL_LINK_STATUS, &success);
- if (!success) {
- glGetProgramInfoLog(object, sizeof(info), NULL, info);
- tigrError(NULL, "shader link error : %s\n", info);
- }
- }
- void tigrCreateShaderProgram(GLStuff* gl, const char* fxSource, int fxSize) {
- if (gl->program != 0) {
- glDeleteProgram(gl->program);
- gl->program = 0;
- }
- GLuint vs = glCreateShader(GL_VERTEX_SHADER);
- const char* vs_source = (const char*)&tigr_upscale_gl_vs;
- glShaderSource(vs, 1, &vs_source, &tigr_upscale_gl_vs_size);
- glCompileShader(vs);
- tigrCheckShaderErrors(vs);
- GLuint fs = glCreateShader(GL_FRAGMENT_SHADER);
- const char* fs_sources[] = {
- (const char*)tigr_upscale_gl_fs,
- fxSource,
- };
- const int fs_lengths[] = {
- tigr_upscale_gl_fs_size,
- fxSize,
- };
- glShaderSource(fs, 2, fs_sources, fs_lengths);
- glCompileShader(fs);
- tigrCheckShaderErrors(fs);
- gl->program = glCreateProgram();
- glAttachShader(gl->program, vs);
- glAttachShader(gl->program, fs);
- glLinkProgram(gl->program);
- tigrCheckProgramErrors(gl->program);
- glDeleteShader(vs);
- glDeleteShader(fs);
- gl->uniform_projection = glGetUniformLocation(gl->program, "projection");
- gl->uniform_model = glGetUniformLocation(gl->program, "model");
- gl->uniform_parameters = glGetUniformLocation(gl->program, "parameters");
- }
- void tigrGAPICreate(Tigr* bmp) {
- TigrInternal* win = tigrInternal(bmp);
- GLStuff* gl = &win->gl;
- GLuint VBO;
- GLfloat vertices[] = { // pos uv
- 0.0f, 1.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,
- 0.0f, 1.0f, 0.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f, 0.0f
- };
- #ifdef _WIN32
- if (tigrGL11Init(bmp))
- return;
- tigrGL33Init(bmp);
- #endif
- if (!gl->gl_legacy) {
- // create vao
- glGenVertexArrays(1, &gl->vao);
- glGenBuffers(1, &VBO);
- glBindBuffer(GL_ARRAY_BUFFER, VBO);
- glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
- glBindVertexArray(gl->vao);
- glEnableVertexAttribArray(0);
- glEnableVertexAttribArray(1);
- glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(GLfloat), NULL);
- glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(GLfloat), NULL);
- // create program
- tigrCreateShaderProgram(gl, tigr_default_fx_gl_fs, tigr_default_fx_gl_fs_size);
- }
- // create textures
- if (gl->gl_legacy) {
- glEnable(GL_TEXTURE_2D);
- }
- glGenTextures(2, gl->tex);
- for (int i = 0; i < 2; ++i) {
- glBindTexture(GL_TEXTURE_2D, gl->tex[i]);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gl->gl_legacy ? GL_NEAREST : GL_LINEAR);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gl->gl_legacy ? GL_NEAREST : GL_LINEAR);
- glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
- glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
- }
- tigrCheckGLError("initialization");
- }
- void tigrGAPIDestroy(Tigr* bmp) {
- TigrInternal* win = tigrInternal(bmp);
- GLStuff* gl = &win->gl;
- if (tigrGAPIBegin(bmp) < 0) {
- tigrError(bmp, "Cannot activate OpenGL context.\n");
- return;
- }
- if (!gl->gl_legacy) {
- glDeleteTextures(2, gl->tex);
- glDeleteProgram(gl->program);
- }
- tigrCheckGLError("destroy");
- if (tigrGAPIEnd(bmp) < 0) {
- tigrError(bmp, "Cannot deactivate OpenGL context.\n");
- return;
- }
- }
- void tigrGAPIDraw(int legacy, GLuint uniform_model, GLuint tex, Tigr* bmp, int x1, int y1, int x2, int y2) {
- glBindTexture(GL_TEXTURE_2D, tex);
- glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, bmp->w, bmp->h, 0, GL_RGBA, GL_UNSIGNED_BYTE, bmp->pix);
- if (!legacy) {
- float sx = (float)(x2 - x1);
- float sy = (float)(y2 - y1);
- float tx = (float)x1;
- float ty = (float)y1;
- float model[16] = { sx, 0.0f, 0.0f, 0.0f, 0.0f, sy, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, tx, ty, 0.0f, 1.0f };
- glUniformMatrix4fv(uniform_model, 1, GL_FALSE, model);
- glDrawArrays(GL_TRIANGLES, 0, 6);
- } else {
- #if !(__APPLE__ || __ANDROID__)
- glBegin(GL_QUADS);
- glTexCoord2f(1.0f, 0.0f);
- glVertex2i(x2, y1);
- glTexCoord2f(0.0f, 0.0f);
- glVertex2i(x1, y1);
- glTexCoord2f(0.0f, 1.0f);
- glVertex2i(x1, y2);
- glTexCoord2f(1.0f, 1.0f);
- glVertex2i(x2, y2);
- glEnd();
- #else
- assert(0);
- #endif
- }
- }
- void tigrGAPIPresent(Tigr* bmp, int w, int h) {
- TigrInternal* win = tigrInternal(bmp);
- GLStuff* gl = &win->gl;
- glViewport(0, 0, w, h);
- if (!gl->gl_user_opengl_rendering) {
- glClearColor(0, 0, 0, 1);
- glClear(GL_COLOR_BUFFER_BIT);
- }
- if (!gl->gl_legacy) {
- float projection[16] = { 2.0f / w, 0.0f, 0.0f, 0.0f, 0.0f, -2.0f / h, 0.0f, 0.0f,
- 0.0f, 0.0f, 1.0f, 0.0f, -1.0f, 1.0f, 0.0f, 1.0f };
- glActiveTexture(GL_TEXTURE0);
- glBindVertexArray(gl->vao);
- glUseProgram(gl->program);
- glUniformMatrix4fv(gl->uniform_projection, 1, GL_FALSE, projection);
- glUniform4f(gl->uniform_parameters, win->p1, win->p2, win->p3, win->p4);
- } else {
- #if !(__APPLE__ || __ANDROID__)
- glMatrixMode(GL_PROJECTION);
- glLoadIdentity();
- glOrtho(0, w, h, 0, -1.0f, 1.0f);
- glEnable(GL_TEXTURE_2D);
- #else
- assert(0);
- #endif
- }
- if (gl->gl_user_opengl_rendering) {
- glEnable(GL_BLEND);
- glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
- } else {
- glDisable(GL_BLEND);
- }
- tigrGAPIDraw(gl->gl_legacy, gl->uniform_model, gl->tex[0], bmp, win->pos[0], win->pos[1], win->pos[2], win->pos[3]);
- if (win->widgetsScale > 0) {
- glEnable(GL_BLEND);
- glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
- tigrGAPIDraw(gl->gl_legacy, gl->uniform_model, gl->tex[1], win->widgets,
- (int)(w - win->widgets->w * win->widgetsScale), 0, w, (int)(win->widgets->h * win->widgetsScale));
- }
- tigrCheckGLError("present");
- gl->gl_user_opengl_rendering = 0;
- }
- #endif
- #endif // #ifndef TIGR_HEADLESS
- //////// End of inlined file: tigr_gl.c ////////
- //////// Start of inlined file: tigr_utils.c ////////
- //#include "tigr_internal.h"
- #include <stdio.h>
- #include <stdlib.h>
- #ifndef __ANDROID__
- #ifdef __IOS__
- void* _tigrReadFile(const char* fileName, int* length) {
- #else
- void* tigrReadFile(const char* fileName, int* length) {
- #endif
- // TODO - unicode?
- FILE* file;
- char* data;
- size_t len;
- if (length)
- *length = 0;
- file = fopen(fileName, "rb");
- if (!file)
- return NULL;
- fseek(file, 0, SEEK_END);
- len = ftell(file);
- fseek(file, 0, SEEK_SET);
- data = (char*)malloc(len + 1);
- if (!data) {
- fclose(file);
- return NULL;
- }
- if (fread(data, 1, len, file) != len) {
- free(data);
- fclose(file);
- return NULL;
- }
- data[len] = '\0';
- fclose(file);
- if (length)
- *length = len;
- return data;
- }
- #endif // __ANDROID__
- // Reads a single UTF8 codepoint.
- const char* tigrDecodeUTF8(const char* text, int* cp) {
- unsigned char c = *text++;
- int extra = 0, min = 0;
- *cp = 0;
- if (c >= 0xf0) {
- *cp = c & 0x07;
- extra = 3;
- min = 0x10000;
- } else if (c >= 0xe0) {
- *cp = c & 0x0f;
- extra = 2;
- min = 0x800;
- } else if (c >= 0xc0) {
- *cp = c & 0x1f;
- extra = 1;
- min = 0x80;
- } else if (c >= 0x80) {
- *cp = 0xfffd;
- } else {
- *cp = c;
- }
- while (extra--) {
- c = *text++;
- if ((c & 0xc0) != 0x80) {
- *cp = 0xfffd;
- break;
- }
- (*cp) = ((*cp) << 6) | (c & 0x3f);
- }
- if (*cp < min) {
- *cp = 0xfffd;
- }
- return text;
- }
- char* tigrEncodeUTF8(char* text, int cp) {
- if (cp < 0 || cp > 0x10ffff) {
- cp = 0xfffd;
- }
- #define EMIT(X, Y, Z) *text++ = X | ((cp >> Y) & Z)
- if (cp < 0x80) {
- EMIT(0x00, 0, 0x7f);
- } else if (cp < 0x800) {
- EMIT(0xc0, 6, 0x1f);
- EMIT(0x80, 0, 0x3f);
- } else if (cp < 0x10000) {
- EMIT(0xe0, 12, 0xf);
- EMIT(0x80, 6, 0x3f);
- EMIT(0x80, 0, 0x3f);
- } else {
- EMIT(0xf0, 18, 0x7);
- EMIT(0x80, 12, 0x3f);
- EMIT(0x80, 6, 0x3f);
- EMIT(0x80, 0, 0x3f);
- }
- return text;
- #undef EMIT
- }
- #ifndef TIGR_HEADLESS
- int tigrBeginOpenGL(Tigr* bmp) {
- #ifdef TIGR_GAPI_GL
- TigrInternal* win = tigrInternal(bmp);
- win->gl.gl_user_opengl_rendering = 1;
- return tigrGAPIBegin(bmp) == 0;
- #else
- return 0;
- #endif
- }
- void tigrSetPostShader(Tigr* bmp, const char* code, int size) {
- #ifdef TIGR_GAPI_GL
- tigrGAPIBegin(bmp);
- TigrInternal* win = tigrInternal(bmp);
- GLStuff* gl = &win->gl;
- tigrCreateShaderProgram(gl, code, size);
- tigrGAPIEnd(bmp);
- #endif
- }
- void tigrSetPostFX(Tigr* bmp, float p1, float p2, float p3, float p4) {
- TigrInternal* win = tigrInternal(bmp);
- win->p1 = p1;
- win->p2 = p2;
- win->p3 = p3;
- win->p4 = p4;
- }
- #endif // TIGR_HEADLESS
- //////// End of inlined file: tigr_utils.c ////////
- //////// End of inlined file: tigr_amalgamated.c ////////
|