core.c 260 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882388338843885388638873888388938903891389238933894389538963897389838993900390139023903390439053906390739083909391039113912391339143915391639173918391939203921392239233924392539263927392839293930393139323933393439353936393739383939394039413942394339443945394639473948394939503951395239533954395539563957395839593960396139623963396439653966396739683969397039713972397339743975397639773978397939803981398239833984398539863987398839893990399139923993399439953996399739983999400040014002400340044005400640074008400940104011401240134014401540164017401840194020402140224023402440254026402740284029403040314032403340344035403640374038403940404041404240434044404540464047404840494050405140524053405440554056405740584059406040614062406340644065406640674068406940704071407240734074407540764077407840794080408140824083408440854086408740884089409040914092409340944095409640974098409941004101410241034104410541064107410841094110411141124113411441154116411741184119412041214122412341244125412641274128412941304131413241334134413541364137413841394140414141424143414441454146414741484149415041514152415341544155415641574158415941604161416241634164416541664167416841694170417141724173417441754176417741784179418041814182418341844185418641874188418941904191419241934194419541964197419841994200420142024203420442054206420742084209421042114212421342144215421642174218421942204221422242234224422542264227422842294230423142324233423442354236423742384239424042414242424342444245424642474248424942504251425242534254425542564257425842594260426142624263426442654266426742684269427042714272427342744275427642774278427942804281428242834284428542864287428842894290429142924293429442954296429742984299430043014302430343044305430643074308430943104311431243134314431543164317431843194320432143224323432443254326432743284329433043314332433343344335433643374338433943404341434243434344434543464347434843494350435143524353435443554356435743584359436043614362436343644365436643674368436943704371437243734374437543764377437843794380438143824383438443854386438743884389439043914392439343944395439643974398439944004401440244034404440544064407440844094410441144124413441444154416441744184419442044214422442344244425442644274428442944304431443244334434443544364437443844394440444144424443444444454446444744484449445044514452445344544455445644574458445944604461446244634464446544664467446844694470447144724473447444754476447744784479448044814482448344844485448644874488448944904491449244934494449544964497449844994500450145024503450445054506450745084509451045114512451345144515451645174518451945204521452245234524452545264527452845294530453145324533453445354536453745384539454045414542454345444545454645474548454945504551455245534554455545564557455845594560456145624563456445654566456745684569457045714572457345744575457645774578457945804581458245834584458545864587458845894590459145924593459445954596459745984599460046014602460346044605460646074608460946104611461246134614461546164617461846194620462146224623462446254626462746284629463046314632463346344635463646374638463946404641464246434644464546464647464846494650465146524653465446554656465746584659466046614662466346644665466646674668466946704671467246734674467546764677467846794680468146824683468446854686468746884689469046914692469346944695469646974698469947004701470247034704470547064707470847094710471147124713471447154716471747184719472047214722472347244725472647274728472947304731473247334734473547364737473847394740474147424743474447454746474747484749475047514752475347544755475647574758475947604761476247634764476547664767476847694770477147724773477447754776477747784779478047814782478347844785478647874788478947904791479247934794479547964797479847994800480148024803480448054806480748084809481048114812481348144815481648174818481948204821482248234824482548264827482848294830483148324833483448354836483748384839484048414842484348444845484648474848484948504851485248534854485548564857485848594860486148624863486448654866486748684869487048714872487348744875487648774878487948804881488248834884488548864887488848894890489148924893489448954896489748984899490049014902490349044905490649074908490949104911491249134914491549164917491849194920492149224923492449254926492749284929493049314932493349344935493649374938493949404941494249434944494549464947494849494950495149524953495449554956495749584959496049614962496349644965496649674968496949704971497249734974497549764977497849794980498149824983498449854986498749884989499049914992499349944995499649974998499950005001500250035004500550065007500850095010501150125013501450155016501750185019502050215022502350245025502650275028502950305031503250335034503550365037503850395040504150425043504450455046504750485049505050515052505350545055505650575058505950605061506250635064506550665067506850695070507150725073507450755076507750785079508050815082508350845085508650875088508950905091509250935094509550965097509850995100510151025103510451055106510751085109511051115112511351145115511651175118511951205121512251235124512551265127512851295130513151325133513451355136513751385139514051415142514351445145514651475148514951505151515251535154515551565157515851595160516151625163516451655166516751685169517051715172517351745175517651775178517951805181518251835184518551865187518851895190519151925193519451955196519751985199520052015202520352045205520652075208520952105211521252135214521552165217521852195220522152225223522452255226522752285229523052315232523352345235523652375238523952405241524252435244524552465247524852495250525152525253525452555256525752585259526052615262526352645265526652675268526952705271527252735274527552765277527852795280528152825283528452855286528752885289529052915292529352945295529652975298529953005301530253035304530553065307530853095310531153125313531453155316531753185319532053215322532353245325532653275328532953305331533253335334533553365337533853395340534153425343534453455346534753485349535053515352535353545355535653575358535953605361536253635364536553665367536853695370537153725373537453755376537753785379538053815382538353845385538653875388538953905391539253935394539553965397539853995400540154025403540454055406540754085409541054115412541354145415541654175418541954205421542254235424542554265427542854295430543154325433543454355436543754385439544054415442544354445445544654475448544954505451545254535454545554565457545854595460546154625463546454655466546754685469547054715472547354745475547654775478547954805481548254835484548554865487548854895490549154925493549454955496549754985499550055015502550355045505550655075508550955105511551255135514551555165517551855195520552155225523552455255526552755285529553055315532553355345535553655375538553955405541554255435544554555465547554855495550555155525553555455555556555755585559556055615562556355645565556655675568556955705571557255735574557555765577557855795580558155825583558455855586558755885589559055915592559355945595559655975598559956005601560256035604560556065607560856095610561156125613561456155616561756185619562056215622562356245625562656275628562956305631563256335634563556365637563856395640564156425643564456455646564756485649565056515652565356545655565656575658565956605661566256635664566556665667566856695670567156725673567456755676567756785679568056815682568356845685568656875688568956905691569256935694569556965697569856995700570157025703570457055706570757085709571057115712571357145715571657175718571957205721572257235724572557265727572857295730573157325733573457355736573757385739574057415742574357445745574657475748574957505751575257535754575557565757575857595760576157625763576457655766576757685769577057715772577357745775577657775778577957805781578257835784578557865787578857895790579157925793579457955796579757985799580058015802580358045805580658075808580958105811581258135814581558165817581858195820582158225823582458255826582758285829583058315832583358345835583658375838583958405841584258435844584558465847584858495850585158525853585458555856585758585859586058615862586358645865586658675868586958705871587258735874587558765877587858795880588158825883588458855886588758885889589058915892589358945895589658975898589959005901590259035904590559065907590859095910591159125913591459155916591759185919592059215922592359245925592659275928592959305931593259335934593559365937593859395940594159425943594459455946594759485949595059515952595359545955595659575958595959605961596259635964596559665967596859695970597159725973597459755976597759785979598059815982598359845985598659875988598959905991599259935994599559965997599859996000600160026003600460056006600760086009601060116012601360146015601660176018601960206021602260236024602560266027602860296030603160326033603460356036603760386039604060416042604360446045604660476048604960506051605260536054605560566057605860596060606160626063606460656066606760686069607060716072607360746075607660776078607960806081608260836084608560866087608860896090609160926093609460956096609760986099610061016102610361046105610661076108610961106111611261136114611561166117611861196120612161226123612461256126612761286129613061316132613361346135613661376138613961406141614261436144614561466147614861496150615161526153615461556156615761586159616061616162616361646165616661676168616961706171617261736174617561766177617861796180618161826183618461856186618761886189619061916192619361946195619661976198619962006201620262036204620562066207620862096210621162126213621462156216621762186219622062216222622362246225622662276228622962306231623262336234623562366237623862396240624162426243624462456246624762486249625062516252625362546255625662576258625962606261626262636264626562666267626862696270627162726273627462756276627762786279628062816282628362846285628662876288628962906291629262936294629562966297629862996300630163026303630463056306630763086309631063116312631363146315631663176318631963206321632263236324632563266327632863296330633163326333633463356336633763386339634063416342634363446345634663476348634963506351635263536354635563566357635863596360636163626363636463656366636763686369637063716372637363746375637663776378637963806381638263836384638563866387638863896390639163926393639463956396639763986399640064016402640364046405640664076408640964106411641264136414641564166417641864196420642164226423642464256426642764286429643064316432643364346435643664376438643964406441644264436444644564466447644864496450645164526453645464556456645764586459646064616462646364646465646664676468646964706471647264736474647564766477647864796480648164826483648464856486648764886489649064916492649364946495649664976498649965006501650265036504650565066507650865096510651165126513651465156516651765186519652065216522652365246525652665276528652965306531653265336534653565366537653865396540654165426543654465456546654765486549655065516552655365546555655665576558655965606561656265636564656565666567656865696570657165726573657465756576657765786579658065816582658365846585658665876588658965906591659265936594659565966597659865996600660166026603660466056606660766086609661066116612661366146615661666176618661966206621662266236624662566266627662866296630
  1. /**********************************************************************************************
  2. *
  3. * raylib.core - Basic functions to manage windows, OpenGL context and input on multiple platforms
  4. *
  5. * PLATFORMS SUPPORTED:
  6. * - PLATFORM_DESKTOP: Windows (Win32, Win64)
  7. * - PLATFORM_DESKTOP: Linux (X11 desktop mode)
  8. * - PLATFORM_DESKTOP: FreeBSD, OpenBSD, NetBSD, DragonFly (X11 desktop)
  9. * - PLATFORM_DESKTOP: OSX/macOS
  10. * - PLATFORM_ANDROID: Android (ARM, ARM64)
  11. * - PLATFORM_RPI: Raspberry Pi 0,1,2,3 (Raspbian, native mode)
  12. * - PLATFORM_DRM: Linux native mode, including Raspberry Pi 4 with V3D fkms driver
  13. * - PLATFORM_WEB: HTML5 with WebAssembly
  14. *
  15. * CONFIGURATION:
  16. *
  17. * #define PLATFORM_DESKTOP
  18. * Windowing and input system configured for desktop platforms: Windows, Linux, OSX, FreeBSD, OpenBSD, NetBSD, DragonFly
  19. * NOTE: Oculus Rift CV1 requires PLATFORM_DESKTOP for mirror rendering - View [rlgl] module to enable it
  20. *
  21. * #define PLATFORM_ANDROID
  22. * Windowing and input system configured for Android device, app activity managed internally in this module.
  23. * NOTE: OpenGL ES 2.0 is required and graphic device is managed by EGL
  24. *
  25. * #define PLATFORM_RPI
  26. * Windowing and input system configured for Raspberry Pi in native mode (no XWindow required),
  27. * graphic device is managed by EGL and inputs are processed is raw mode, reading from /dev/input/
  28. *
  29. * #define PLATFORM_DRM
  30. * Windowing and input system configured for DRM native mode (RPI4 and other devices)
  31. * graphic device is managed by EGL and inputs are processed is raw mode, reading from /dev/input/
  32. *
  33. * #define PLATFORM_WEB
  34. * Windowing and input system configured for HTML5 (run on browser), code converted from C to asm.js
  35. * using emscripten compiler. OpenGL ES 2.0 required for direct translation to WebGL equivalent code.
  36. *
  37. * #define SUPPORT_DEFAULT_FONT (default)
  38. * Default font is loaded on window initialization to be available for the user to render simple text.
  39. * NOTE: If enabled, uses external module functions to load default raylib font (module: text)
  40. *
  41. * #define SUPPORT_CAMERA_SYSTEM
  42. * Camera module is included (camera.h) and multiple predefined cameras are available: free, 1st/3rd person, orbital
  43. *
  44. * #define SUPPORT_GESTURES_SYSTEM
  45. * Gestures module is included (gestures.h) to support gestures detection: tap, hold, swipe, drag
  46. *
  47. * #define SUPPORT_MOUSE_GESTURES
  48. * Mouse gestures are directly mapped like touches and processed by gestures system.
  49. *
  50. * #define SUPPORT_TOUCH_AS_MOUSE
  51. * Touch input and mouse input are shared. Mouse functions also return touch information.
  52. *
  53. * #define SUPPORT_SSH_KEYBOARD_RPI (Raspberry Pi only)
  54. * Reconfigure standard input to receive key inputs, works with SSH connection.
  55. * WARNING: Reconfiguring standard input could lead to undesired effects, like breaking other running processes or
  56. * blocking the device if not restored properly. Use with care.
  57. *
  58. * #define SUPPORT_MOUSE_CURSOR_POINT
  59. * Draw a mouse pointer on screen
  60. *
  61. * #define SUPPORT_BUSY_WAIT_LOOP
  62. * Use busy wait loop for timing sync, if not defined, a high-resolution timer is setup and used
  63. *
  64. * #define SUPPORT_PARTIALBUSY_WAIT_LOOP
  65. * Use a partial-busy wait loop, in this case frame sleeps for most of the time and runs a busy-wait-loop at the end
  66. *
  67. * #define SUPPORT_EVENTS_WAITING
  68. * Wait for events passively (sleeping while no events) instead of polling them actively every frame
  69. *
  70. * #define SUPPORT_SCREEN_CAPTURE
  71. * Allow automatic screen capture of current screen pressing F12, defined in KeyCallback()
  72. *
  73. * #define SUPPORT_GIF_RECORDING
  74. * Allow automatic gif recording of current screen pressing CTRL+F12, defined in KeyCallback()
  75. *
  76. * #define SUPPORT_COMPRESSION_API
  77. * Support CompressData() and DecompressData() functions, those functions use zlib implementation
  78. * provided by stb_image and stb_image_write libraries, so, those libraries must be enabled on textures module
  79. * for linkage
  80. *
  81. * #define SUPPORT_DATA_STORAGE
  82. * Support saving binary data automatically to a generated storage.data file. This file is managed internally
  83. *
  84. * #define SUPPORT_EVENTS_AUTOMATION
  85. * Support automatic generated events, loading and recording of those events when required
  86. *
  87. * DEPENDENCIES:
  88. * rglfw - Manage graphic device, OpenGL context and inputs on PLATFORM_DESKTOP (Windows, Linux, OSX. FreeBSD, OpenBSD, NetBSD, DragonFly)
  89. * raymath - 3D math functionality (Vector2, Vector3, Matrix, Quaternion)
  90. * camera - Multiple 3D camera modes (free, orbital, 1st person, 3rd person)
  91. * gestures - Gestures system for touch-ready devices (or simulated from mouse inputs)
  92. *
  93. *
  94. * LICENSE: zlib/libpng
  95. *
  96. * Copyright (c) 2013-2021 Ramon Santamaria (@raysan5)
  97. *
  98. * This software is provided "as-is", without any express or implied warranty. In no event
  99. * will the authors be held liable for any damages arising from the use of this software.
  100. *
  101. * Permission is granted to anyone to use this software for any purpose, including commercial
  102. * applications, and to alter it and redistribute it freely, subject to the following restrictions:
  103. *
  104. * 1. The origin of this software must not be misrepresented; you must not claim that you
  105. * wrote the original software. If you use this software in a product, an acknowledgment
  106. * in the product documentation would be appreciated but is not required.
  107. *
  108. * 2. Altered source versions must be plainly marked as such, and must not be misrepresented
  109. * as being the original software.
  110. *
  111. * 3. This notice may not be removed or altered from any source distribution.
  112. *
  113. **********************************************************************************************/
  114. #include "raylib.h" // Declares module functions
  115. // Check if config flags have been externally provided on compilation line
  116. #if !defined(EXTERNAL_CONFIG_FLAGS)
  117. #include "config.h" // Defines module configuration flags
  118. #endif
  119. #include "utils.h" // Required for: TRACELOG() macros
  120. #define RLGL_IMPLEMENTATION
  121. #include "rlgl.h" // OpenGL abstraction layer to OpenGL 1.1, 3.3+ or ES2
  122. #define RAYMATH_IMPLEMENTATION // Define external out-of-line implementation
  123. #include "raymath.h" // Vector3, Quaternion and Matrix functionality
  124. #if defined(SUPPORT_GESTURES_SYSTEM)
  125. #define GESTURES_IMPLEMENTATION
  126. #include "gestures.h" // Gestures detection functionality
  127. #endif
  128. #if defined(SUPPORT_CAMERA_SYSTEM)
  129. #define CAMERA_IMPLEMENTATION
  130. #include "camera.h" // Camera system functionality
  131. #endif
  132. #if defined(SUPPORT_GIF_RECORDING)
  133. #define MSF_GIF_MALLOC(contextPointer, newSize) RL_MALLOC(newSize)
  134. #define MSF_GIF_REALLOC(contextPointer, oldMemory, oldSize, newSize) RL_REALLOC(oldMemory, newSize)
  135. #define MSF_GIF_FREE(contextPointer, oldMemory, oldSize) RL_FREE(oldMemory)
  136. #define MSF_GIF_IMPL
  137. #include "external/msf_gif.h" // GIF recording functionality
  138. #endif
  139. #if defined(SUPPORT_COMPRESSION_API)
  140. #define SINFL_IMPLEMENTATION
  141. #include "external/sinfl.h" // Deflate (RFC 1951) decompressor
  142. #define SDEFL_IMPLEMENTATION
  143. #include "external/sdefl.h" // Deflate (RFC 1951) compressor
  144. #endif
  145. #if (defined(__linux__) || defined(PLATFORM_WEB)) && _POSIX_C_SOURCE < 199309L
  146. #undef _POSIX_C_SOURCE
  147. #define _POSIX_C_SOURCE 199309L // Required for: CLOCK_MONOTONIC if compiled with c99 without gnu ext.
  148. #endif
  149. #include <stdlib.h> // Required for: srand(), rand(), atexit()
  150. #include <stdio.h> // Required for: sprintf() [Used in OpenURL()]
  151. #include <string.h> // Required for: strrchr(), strcmp(), strlen()
  152. #include <time.h> // Required for: time() [Used in InitTimer()]
  153. #include <math.h> // Required for: tan() [Used in BeginMode3D()], atan2f() [Used in LoadVrStereoConfig()]
  154. #include <sys/stat.h> // Required for: stat() [Used in GetFileModTime()]
  155. #if defined(PLATFORM_DESKTOP) && defined(_WIN32) && (defined(_MSC_VER) || defined(__TINYC__))
  156. #define DIRENT_MALLOC RL_MALLOC
  157. #define DIRENT_FREE RL_FREE
  158. #include "external/dirent.h" // Required for: DIR, opendir(), closedir() [Used in GetDirectoryFiles()]
  159. #else
  160. #include <dirent.h> // Required for: DIR, opendir(), closedir() [Used in GetDirectoryFiles()]
  161. #endif
  162. #if defined(_WIN32)
  163. #include <direct.h> // Required for: _getch(), _chdir()
  164. #define GETCWD _getcwd // NOTE: MSDN recommends not to use getcwd(), chdir()
  165. #define CHDIR _chdir
  166. #include <io.h> // Required for: _access() [Used in FileExists()]
  167. #else
  168. #include <unistd.h> // Required for: getch(), chdir() (POSIX), access()
  169. #define GETCWD getcwd
  170. #define CHDIR chdir
  171. #endif
  172. #if defined(PLATFORM_DESKTOP)
  173. #define GLFW_INCLUDE_NONE // Disable the standard OpenGL header inclusion on GLFW3
  174. // NOTE: Already provided by rlgl implementation (on glad.h)
  175. #include "GLFW/glfw3.h" // GLFW3 library: Windows, OpenGL context and Input management
  176. // NOTE: GLFW3 already includes gl.h (OpenGL) headers
  177. // Support retrieving native window handlers
  178. #if defined(_WIN32)
  179. #define GLFW_EXPOSE_NATIVE_WIN32
  180. #include "GLFW/glfw3native.h" // WARNING: It requires customization to avoid windows.h inclusion!
  181. #if defined(SUPPORT_WINMM_HIGHRES_TIMER) && !defined(SUPPORT_BUSY_WAIT_LOOP)
  182. // NOTE: Those functions require linking with winmm library
  183. unsigned int __stdcall timeBeginPeriod(unsigned int uPeriod);
  184. unsigned int __stdcall timeEndPeriod(unsigned int uPeriod);
  185. #endif
  186. #endif
  187. #if defined(__linux__) || defined(__FreeBSD__)
  188. #include <sys/time.h> // Required for: timespec, nanosleep(), select() - POSIX
  189. //#define GLFW_EXPOSE_NATIVE_X11 // WARNING: Exposing Xlib.h > X.h results in dup symbols for Font type
  190. //#define GLFW_EXPOSE_NATIVE_WAYLAND
  191. //#define GLFW_EXPOSE_NATIVE_MIR
  192. #include "GLFW/glfw3native.h" // Required for: glfwGetX11Window()
  193. #endif
  194. #if defined(__APPLE__)
  195. #include <unistd.h> // Required for: usleep()
  196. //#define GLFW_EXPOSE_NATIVE_COCOA // WARNING: Fails due to type redefinition
  197. #include "GLFW/glfw3native.h" // Required for: glfwGetCocoaWindow()
  198. #endif
  199. #endif
  200. #if defined(PLATFORM_ANDROID)
  201. //#include <android/sensor.h> // Required for: Android sensors functions (accelerometer, gyroscope, light...)
  202. #include <android/window.h> // Required for: AWINDOW_FLAG_FULLSCREEN definition and others
  203. #include <android_native_app_glue.h> // Required for: android_app struct and activity management
  204. #include <EGL/egl.h> // Native platform windowing system interface
  205. //#include <GLES2/gl2.h> // OpenGL ES 2.0 library (not required in this module, only in rlgl)
  206. #endif
  207. #if defined(PLATFORM_RPI) || defined(PLATFORM_DRM)
  208. #include <fcntl.h> // POSIX file control definitions - open(), creat(), fcntl()
  209. #include <unistd.h> // POSIX standard function definitions - read(), close(), STDIN_FILENO
  210. #include <termios.h> // POSIX terminal control definitions - tcgetattr(), tcsetattr()
  211. #include <pthread.h> // POSIX threads management (inputs reading)
  212. #include <dirent.h> // POSIX directory browsing
  213. #include <sys/ioctl.h> // Required for: ioctl() - UNIX System call for device-specific input/output operations
  214. #include <linux/kd.h> // Linux: KDSKBMODE, K_MEDIUMRAM constants definition
  215. #include <linux/input.h> // Linux: Keycodes constants definition (KEY_A, ...)
  216. #include <linux/joystick.h> // Linux: Joystick support library
  217. #if defined(PLATFORM_RPI)
  218. #include "bcm_host.h" // Raspberry Pi VideoCore IV access functions
  219. #endif
  220. #if defined(PLATFORM_DRM)
  221. #include <gbm.h> // Generic Buffer Management (native platform for EGL on DRM)
  222. #include <xf86drm.h> // Direct Rendering Manager user-level library interface
  223. #include <xf86drmMode.h> // Direct Rendering Manager mode setting (KMS) interface
  224. #endif
  225. #include "EGL/egl.h" // Native platform windowing system interface
  226. #include "EGL/eglext.h" // EGL extensions
  227. //#include "GLES2/gl2.h" // OpenGL ES 2.0 library (not required in this module, only in rlgl)
  228. #endif
  229. #if defined(PLATFORM_WEB)
  230. #define GLFW_INCLUDE_ES2 // GLFW3: Enable OpenGL ES 2.0 (translated to WebGL)
  231. #include "GLFW/glfw3.h" // GLFW3: Windows, OpenGL context and Input management
  232. #include <sys/time.h> // Required for: timespec, nanosleep(), select() - POSIX
  233. #include <emscripten/emscripten.h> // Emscripten functionality for C
  234. #include <emscripten/html5.h> // Emscripten HTML5 library
  235. #endif
  236. //----------------------------------------------------------------------------------
  237. // Defines and Macros
  238. //----------------------------------------------------------------------------------
  239. #if defined(PLATFORM_RPI) || defined(PLATFORM_DRM)
  240. #define USE_LAST_TOUCH_DEVICE // When multiple touchscreens are connected, only use the one with the highest event<N> number
  241. #define DEFAULT_GAMEPAD_DEV "/dev/input/js" // Gamepad input (base dev for all gamepads: js0, js1, ...)
  242. #define DEFAULT_EVDEV_PATH "/dev/input/" // Path to the linux input events
  243. #endif
  244. #ifndef MAX_FILEPATH_LENGTH
  245. #if defined(__linux__)
  246. #define MAX_FILEPATH_LENGTH 4096 // Maximum length for filepaths (Linux PATH_MAX default value)
  247. #else
  248. #define MAX_FILEPATH_LENGTH 512 // Maximum length supported for filepaths
  249. #endif
  250. #endif
  251. #ifndef MAX_KEYBOARD_KEYS
  252. #define MAX_KEYBOARD_KEYS 512 // Maximum number of keyboard keys supported
  253. #endif
  254. #ifndef MAX_MOUSE_BUTTONS
  255. #define MAX_MOUSE_BUTTONS 8 // Maximum number of mouse buttons supported
  256. #endif
  257. #ifndef MAX_GAMEPADS
  258. #define MAX_GAMEPADS 4 // Maximum number of gamepads supported
  259. #endif
  260. #ifndef MAX_GAMEPAD_AXIS
  261. #define MAX_GAMEPAD_AXIS 8 // Maximum number of axis supported (per gamepad)
  262. #endif
  263. #ifndef MAX_GAMEPAD_BUTTONS
  264. #define MAX_GAMEPAD_BUTTONS 32 // Maximum number of buttons supported (per gamepad)
  265. #endif
  266. #ifndef MAX_TOUCH_POINTS
  267. #define MAX_TOUCH_POINTS 8 // Maximum number of touch points supported
  268. #endif
  269. #ifndef MAX_KEY_PRESSED_QUEUE
  270. #define MAX_KEY_PRESSED_QUEUE 16 // Maximum number of keys in the key input queue
  271. #endif
  272. #ifndef MAX_CHAR_PRESSED_QUEUE
  273. #define MAX_CHAR_PRESSED_QUEUE 16 // Maximum number of characters in the char input queue
  274. #endif
  275. #if defined(SUPPORT_DATA_STORAGE)
  276. #ifndef STORAGE_DATA_FILE
  277. #define STORAGE_DATA_FILE "storage.data" // Automatic storage filename
  278. #endif
  279. #endif
  280. #ifndef MAX_DECOMPRESSION_SIZE
  281. #define MAX_DECOMPRESSION_SIZE 64 // Maximum size allocated for decompression in MB
  282. #endif
  283. // Flags operation macros
  284. #define FLAG_SET(n, f) ((n) |= (f))
  285. #define FLAG_CLEAR(n, f) ((n) &= ~(f))
  286. #define FLAG_TOGGLE(n, f) ((n) ^= (f))
  287. #define FLAG_CHECK(n, f) ((n) & (f))
  288. //----------------------------------------------------------------------------------
  289. // Types and Structures Definition
  290. //----------------------------------------------------------------------------------
  291. #if defined(PLATFORM_RPI) || defined(PLATFORM_DRM)
  292. typedef struct {
  293. pthread_t threadId; // Event reading thread id
  294. int fd; // File descriptor to the device it is assigned to
  295. int eventNum; // Number of 'event<N>' device
  296. Rectangle absRange; // Range of values for absolute pointing devices (touchscreens)
  297. int touchSlot; // Hold the touch slot number of the currently being sent multitouch block
  298. bool isMouse; // True if device supports relative X Y movements
  299. bool isTouch; // True if device supports absolute X Y movements and has BTN_TOUCH
  300. bool isMultitouch; // True if device supports multiple absolute movevents and has BTN_TOUCH
  301. bool isKeyboard; // True if device has letter keycodes
  302. bool isGamepad; // True if device has gamepad buttons
  303. } InputEventWorker;
  304. #endif
  305. typedef struct { int x; int y; } Point;
  306. typedef struct { unsigned int width; unsigned int height; } Size;
  307. // Core global state context data
  308. typedef struct CoreData {
  309. struct {
  310. #if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB)
  311. GLFWwindow *handle; // GLFW window handle (graphic device)
  312. #endif
  313. #if defined(PLATFORM_RPI)
  314. EGL_DISPMANX_WINDOW_T handle; // Native window handle (graphic device)
  315. #endif
  316. #if defined(PLATFORM_ANDROID) || defined(PLATFORM_RPI) || defined(PLATFORM_DRM)
  317. #if defined(PLATFORM_DRM)
  318. int fd; // File descriptor for /dev/dri/...
  319. drmModeConnector *connector; // Direct Rendering Manager (DRM) mode connector
  320. drmModeCrtc *crtc; // CRT Controller
  321. int modeIndex; // Index of the used mode of connector->modes
  322. struct gbm_device *gbmDevice; // GBM device
  323. struct gbm_surface *gbmSurface; // GBM surface
  324. struct gbm_bo *prevBO; // Previous GBM buffer object (during frame swapping)
  325. uint32_t prevFB; // Previous GBM framebufer (during frame swapping)
  326. #endif // PLATFORM_DRM
  327. EGLDisplay device; // Native display device (physical screen connection)
  328. EGLSurface surface; // Surface to draw on, framebuffers (connected to context)
  329. EGLContext context; // Graphic context, mode in which drawing can be done
  330. EGLConfig config; // Graphic config
  331. #endif
  332. const char *title; // Window text title const pointer
  333. unsigned int flags; // Configuration flags (bit based), keeps window state
  334. bool ready; // Check if window has been initialized successfully
  335. bool fullscreen; // Check if fullscreen mode is enabled
  336. bool shouldClose; // Check if window set for closing
  337. bool resizedLastFrame; // Check if window has been resized last frame
  338. Point position; // Window position on screen (required on fullscreen toggle)
  339. Size display; // Display width and height (monitor, device-screen, LCD, ...)
  340. Size screen; // Screen width and height (used render area)
  341. Size currentFbo; // Current render width and height (depends on active fbo)
  342. Size render; // Framebuffer width and height (render area, including black bars if required)
  343. Point renderOffset; // Offset from render area (must be divided by 2)
  344. Matrix screenScale; // Matrix to scale screen (framebuffer rendering)
  345. char **dropFilesPath; // Store dropped files paths as strings
  346. int dropFileCount; // Count dropped files strings
  347. } Window;
  348. #if defined(PLATFORM_ANDROID)
  349. struct {
  350. bool appEnabled; // Flag to detect if app is active ** = true
  351. struct android_app *app; // Android activity
  352. struct android_poll_source *source; // Android events polling source
  353. bool contextRebindRequired; // Used to know context rebind required
  354. } Android;
  355. #endif
  356. struct {
  357. const char *basePath; // Base path for data storage
  358. } Storage;
  359. struct {
  360. #if defined(PLATFORM_RPI) || defined(PLATFORM_DRM)
  361. InputEventWorker eventWorker[10]; // List of worker threads for every monitored "/dev/input/event<N>"
  362. #endif
  363. struct {
  364. int exitKey; // Default exit key
  365. char currentKeyState[MAX_KEYBOARD_KEYS]; // Registers current frame key state
  366. char previousKeyState[MAX_KEYBOARD_KEYS]; // Registers previous frame key state
  367. int keyPressedQueue[MAX_KEY_PRESSED_QUEUE]; // Input keys queue
  368. int keyPressedQueueCount; // Input keys queue count
  369. int charPressedQueue[MAX_CHAR_PRESSED_QUEUE]; // Input characters queue (unicode)
  370. int charPressedQueueCount; // Input characters queue count
  371. #if defined(PLATFORM_RPI) || defined(PLATFORM_DRM)
  372. int defaultMode; // Default keyboard mode
  373. #if defined(SUPPORT_SSH_KEYBOARD_RPI)
  374. bool evtMode; // Keyboard in event mode
  375. #endif
  376. int defaultFileFlags; // Default IO file flags
  377. struct termios defaultSettings; // Default keyboard settings
  378. int fd; // File descriptor for the evdev keyboard
  379. #endif
  380. } Keyboard;
  381. struct {
  382. Vector2 offset; // Mouse offset
  383. Vector2 scale; // Mouse scaling
  384. Vector2 currentPosition; // Mouse position on screen
  385. Vector2 previousPosition; // Previous mouse position
  386. int cursor; // Tracks current mouse cursor
  387. bool cursorHidden; // Track if cursor is hidden
  388. bool cursorOnScreen; // Tracks if cursor is inside client area
  389. char currentButtonState[MAX_MOUSE_BUTTONS]; // Registers current mouse button state
  390. char previousButtonState[MAX_MOUSE_BUTTONS]; // Registers previous mouse button state
  391. float currentWheelMove; // Registers current mouse wheel variation
  392. float previousWheelMove; // Registers previous mouse wheel variation
  393. #if defined(PLATFORM_RPI) || defined(PLATFORM_DRM)
  394. char currentButtonStateEvdev[MAX_MOUSE_BUTTONS]; // Holds the new mouse state for the next polling event to grab (Can't be written directly due to multithreading, app could miss the update)
  395. #endif
  396. } Mouse;
  397. struct {
  398. int pointCount; // Number of touch points active
  399. int pointId[MAX_TOUCH_POINTS]; // Point identifiers
  400. Vector2 position[MAX_TOUCH_POINTS]; // Touch position on screen
  401. char currentTouchState[MAX_TOUCH_POINTS]; // Registers current touch state
  402. char previousTouchState[MAX_TOUCH_POINTS]; // Registers previous touch state
  403. } Touch;
  404. struct {
  405. int lastButtonPressed; // Register last gamepad button pressed
  406. int axisCount; // Register number of available gamepad axis
  407. bool ready[MAX_GAMEPADS]; // Flag to know if gamepad is ready
  408. char currentButtonState[MAX_GAMEPADS][MAX_GAMEPAD_BUTTONS]; // Current gamepad buttons state
  409. char previousButtonState[MAX_GAMEPADS][MAX_GAMEPAD_BUTTONS]; // Previous gamepad buttons state
  410. float axisState[MAX_GAMEPADS][MAX_GAMEPAD_AXIS]; // Gamepad axis state
  411. #if defined(PLATFORM_RPI) || defined(PLATFORM_DRM) || defined(PLATFORM_WEB)
  412. pthread_t threadId; // Gamepad reading thread id
  413. int streamId[MAX_GAMEPADS]; // Gamepad device file descriptor
  414. char name[MAX_GAMEPADS][64]; // Gamepad name holder
  415. #endif
  416. } Gamepad;
  417. } Input;
  418. struct {
  419. double current; // Current time measure
  420. double previous; // Previous time measure
  421. double update; // Time measure for frame update
  422. double draw; // Time measure for frame draw
  423. double frame; // Time measure for one frame
  424. double target; // Desired time for one frame, if 0 not applied
  425. #if defined(PLATFORM_ANDROID) || defined(PLATFORM_RPI) || defined(PLATFORM_DRM)
  426. unsigned long long base; // Base time measure for hi-res timer
  427. #endif
  428. unsigned int frameCounter; // Frame counter
  429. } Time;
  430. } CoreData;
  431. //----------------------------------------------------------------------------------
  432. // Global Variables Definition
  433. //----------------------------------------------------------------------------------
  434. static CoreData CORE = { 0 }; // Global CORE state context
  435. static char **dirFilesPath = NULL; // Store directory files paths as strings
  436. static int dirFileCount = 0; // Count directory files strings
  437. #if defined(SUPPORT_SCREEN_CAPTURE)
  438. static int screenshotCounter = 0; // Screenshots counter
  439. #endif
  440. #if defined(SUPPORT_GIF_RECORDING)
  441. static int gifFrameCounter = 0; // GIF frames counter
  442. static bool gifRecording = false; // GIF recording state
  443. static MsfGifState gifState = { 0 }; // MSGIF context state
  444. #endif
  445. #if defined(SUPPORT_EVENTS_AUTOMATION)
  446. #define MAX_CODE_AUTOMATION_EVENTS 16384
  447. typedef enum AutomationEventType {
  448. EVENT_NONE = 0,
  449. // Input events
  450. INPUT_KEY_UP, // param[0]: key
  451. INPUT_KEY_DOWN, // param[0]: key
  452. INPUT_KEY_PRESSED, // param[0]: key
  453. INPUT_KEY_RELEASED, // param[0]: key
  454. INPUT_MOUSE_BUTTON_UP, // param[0]: button
  455. INPUT_MOUSE_BUTTON_DOWN, // param[0]: button
  456. INPUT_MOUSE_POSITION, // param[0]: x, param[1]: y
  457. INPUT_MOUSE_WHEEL_MOTION, // param[0]: delta
  458. INPUT_GAMEPAD_CONNECT, // param[0]: gamepad
  459. INPUT_GAMEPAD_DISCONNECT, // param[0]: gamepad
  460. INPUT_GAMEPAD_BUTTON_UP, // param[0]: button
  461. INPUT_GAMEPAD_BUTTON_DOWN, // param[0]: button
  462. INPUT_GAMEPAD_AXIS_MOTION, // param[0]: axis, param[1]: delta
  463. INPUT_TOUCH_UP, // param[0]: id
  464. INPUT_TOUCH_DOWN, // param[0]: id
  465. INPUT_TOUCH_POSITION, // param[0]: x, param[1]: y
  466. INPUT_GESTURE, // param[0]: gesture
  467. // Window events
  468. WINDOW_CLOSE, // no params
  469. WINDOW_MAXIMIZE, // no params
  470. WINDOW_MINIMIZE, // no params
  471. WINDOW_RESIZE, // param[0]: width, param[1]: height
  472. // Custom events
  473. ACTION_TAKE_SCREENSHOT,
  474. ACTION_SETTARGETFPS
  475. } AutomationEventType;
  476. // Event type
  477. // Used to enable events flags
  478. typedef enum {
  479. EVENT_INPUT_KEYBOARD = 0,
  480. EVENT_INPUT_MOUSE = 1,
  481. EVENT_INPUT_GAMEPAD = 2,
  482. EVENT_INPUT_TOUCH = 4,
  483. EVENT_INPUT_GESTURE = 8,
  484. EVENT_WINDOW = 16,
  485. EVENT_CUSTOM = 32
  486. } EventType;
  487. static const char *autoEventTypeName[] = {
  488. "EVENT_NONE",
  489. "INPUT_KEY_UP",
  490. "INPUT_KEY_DOWN",
  491. "INPUT_KEY_PRESSED",
  492. "INPUT_KEY_RELEASED",
  493. "INPUT_MOUSE_BUTTON_UP",
  494. "INPUT_MOUSE_BUTTON_DOWN",
  495. "INPUT_MOUSE_POSITION",
  496. "INPUT_MOUSE_WHEEL_MOTION",
  497. "INPUT_GAMEPAD_CONNECT",
  498. "INPUT_GAMEPAD_DISCONNECT",
  499. "INPUT_GAMEPAD_BUTTON_UP",
  500. "INPUT_GAMEPAD_BUTTON_DOWN",
  501. "INPUT_GAMEPAD_AXIS_MOTION",
  502. "INPUT_TOUCH_UP",
  503. "INPUT_TOUCH_DOWN",
  504. "INPUT_TOUCH_POSITION",
  505. "INPUT_GESTURE",
  506. "WINDOW_CLOSE",
  507. "WINDOW_MAXIMIZE",
  508. "WINDOW_MINIMIZE",
  509. "WINDOW_RESIZE",
  510. "ACTION_TAKE_SCREENSHOT",
  511. "ACTION_SETTARGETFPS"
  512. };
  513. // Automation Event (20 bytes)
  514. typedef struct AutomationEvent {
  515. unsigned int frame; // Event frame
  516. unsigned int type; // Event type (AutoEventType)
  517. int params[3]; // Event parameters (if required)
  518. } AutomationEvent;
  519. static AutomationEvent *events = NULL; // Events array
  520. static unsigned int eventCount = 0; // Events count
  521. static bool eventsPlaying = false; // Play events
  522. static bool eventsRecording = false; // Record events
  523. //static short eventsEnabled = 0b0000001111111111; // Events enabled for checking
  524. #endif
  525. //-----------------------------------------------------------------------------------
  526. //----------------------------------------------------------------------------------
  527. // Other Modules Functions Declaration (required by core)
  528. //----------------------------------------------------------------------------------
  529. #if defined(SUPPORT_DEFAULT_FONT)
  530. extern void LoadFontDefault(void); // [Module: text] Loads default font on InitWindow()
  531. extern void UnloadFontDefault(void); // [Module: text] Unloads default font from GPU memory
  532. #endif
  533. //----------------------------------------------------------------------------------
  534. // Module specific Functions Declaration
  535. //----------------------------------------------------------------------------------
  536. static void InitTimer(void); // Initialize timer (hi-resolution if available)
  537. static bool InitGraphicsDevice(int width, int height); // Initialize graphics device
  538. static void SetupFramebuffer(int width, int height); // Setup main framebuffer
  539. static void SetupViewport(int width, int height); // Set viewport for a provided width and height
  540. #if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB)
  541. static void ErrorCallback(int error, const char *description); // GLFW3 Error Callback, runs on GLFW3 error
  542. // Window callbacks events
  543. static void WindowSizeCallback(GLFWwindow *window, int width, int height); // GLFW3 WindowSize Callback, runs when window is resized
  544. #if !defined(PLATFORM_WEB)
  545. static void WindowMaximizeCallback(GLFWwindow* window, int maximized); // GLFW3 Window Maximize Callback, runs when window is maximized
  546. #endif
  547. static void WindowIconifyCallback(GLFWwindow *window, int iconified); // GLFW3 WindowIconify Callback, runs when window is minimized/restored
  548. static void WindowFocusCallback(GLFWwindow *window, int focused); // GLFW3 WindowFocus Callback, runs when window get/lose focus
  549. static void WindowDropCallback(GLFWwindow *window, int count, const char **paths); // GLFW3 Window Drop Callback, runs when drop files into window
  550. // Input callbacks events
  551. static void KeyCallback(GLFWwindow *window, int key, int scancode, int action, int mods); // GLFW3 Keyboard Callback, runs on key pressed
  552. static void CharCallback(GLFWwindow *window, unsigned int key); // GLFW3 Char Key Callback, runs on key pressed (get char value)
  553. static void MouseButtonCallback(GLFWwindow *window, int button, int action, int mods); // GLFW3 Mouse Button Callback, runs on mouse button pressed
  554. static void MouseCursorPosCallback(GLFWwindow *window, double x, double y); // GLFW3 Cursor Position Callback, runs on mouse move
  555. static void MouseScrollCallback(GLFWwindow *window, double xoffset, double yoffset); // GLFW3 Srolling Callback, runs on mouse wheel
  556. static void CursorEnterCallback(GLFWwindow *window, int enter); // GLFW3 Cursor Enter Callback, cursor enters client area
  557. #endif
  558. #if defined(PLATFORM_ANDROID)
  559. static void AndroidCommandCallback(struct android_app *app, int32_t cmd); // Process Android activity lifecycle commands
  560. static int32_t AndroidInputCallback(struct android_app *app, AInputEvent *event); // Process Android inputs
  561. #endif
  562. #if defined(PLATFORM_WEB)
  563. static EM_BOOL EmscriptenMouseCallback(int eventType, const EmscriptenMouseEvent *mouseEvent, void *userData);
  564. static EM_BOOL EmscriptenTouchCallback(int eventType, const EmscriptenTouchEvent *touchEvent, void *userData);
  565. static EM_BOOL EmscriptenGamepadCallback(int eventType, const EmscriptenGamepadEvent *gamepadEvent, void *userData);
  566. static EM_BOOL EmscriptenResizeCallback(int eventType, const EmscriptenUiEvent *e, void *userData);
  567. #endif
  568. #if defined(PLATFORM_RPI) || defined(PLATFORM_DRM)
  569. static void InitKeyboard(void); // Initialize raw keyboard system
  570. static void RestoreKeyboard(void); // Restore keyboard system
  571. #if defined(SUPPORT_SSH_KEYBOARD_RPI)
  572. static void ProcessKeyboard(void); // Process keyboard events
  573. #endif
  574. static void InitEvdevInput(void); // Initialize evdev inputs
  575. static void ConfigureEvdevDevice(char *device); // Identifies a input device and configures it for use if appropriate
  576. static void PollKeyboardEvents(void); // Process evdev keyboard events.
  577. static void *EventThread(void *arg); // Input device events reading thread
  578. static void InitGamepad(void); // Initialize raw gamepad input
  579. static void *GamepadThread(void *arg); // Mouse reading thread
  580. #if defined(PLATFORM_DRM)
  581. static int FindMatchingConnectorMode(const drmModeConnector *connector, const drmModeModeInfo *mode); // Search matching DRM mode in connector's mode list
  582. static int FindExactConnectorMode(const drmModeConnector *connector, uint width, uint height, uint fps, bool allowInterlaced); // Search exactly matching DRM connector mode in connector's list
  583. static int FindNearestConnectorMode(const drmModeConnector *connector, uint width, uint height, uint fps, bool allowInterlaced); // Search the nearest matching DRM connector mode in connector's list
  584. #endif
  585. #endif // PLATFORM_RPI || PLATFORM_DRM
  586. #if defined(SUPPORT_EVENTS_AUTOMATION)
  587. static void LoadAutomationEvents(const char *fileName); // Load automation events from file
  588. static void ExportAutomationEvents(const char *fileName); // Export recorded automation events into a file
  589. static void RecordAutomationEvent(unsigned int frame); // Record frame events (to internal events array)
  590. static void PlayAutomationEvent(unsigned int frame); // Play frame events (from internal events array)
  591. #endif
  592. #if defined(_WIN32)
  593. // NOTE: We declare Sleep() function symbol to avoid including windows.h (kernel32.lib linkage required)
  594. void __stdcall Sleep(unsigned long msTimeout); // Required for: WaitTime()
  595. #endif
  596. //----------------------------------------------------------------------------------
  597. // Module Functions Definition - Window and OpenGL Context Functions
  598. //----------------------------------------------------------------------------------
  599. #if defined(PLATFORM_ANDROID)
  600. // To allow easier porting to android, we allow the user to define a
  601. // main function which we call from android_main, defined by ourselves
  602. extern int main(int argc, char *argv[]);
  603. void android_main(struct android_app *app)
  604. {
  605. char arg0[] = "raylib"; // NOTE: argv[] are mutable
  606. CORE.Android.app = app;
  607. // TODO: Should we maybe report != 0 return codes somewhere?
  608. (void)main(1, (char *[]) { arg0, NULL });
  609. }
  610. // TODO: Add this to header (if apps really need it)
  611. struct android_app *GetAndroidApp(void)
  612. {
  613. return CORE.Android.app;
  614. }
  615. #endif
  616. // Initialize window and OpenGL context
  617. // NOTE: data parameter could be used to pass any kind of required data to the initialization
  618. void InitWindow(int width, int height, const char *title)
  619. {
  620. TRACELOG(LOG_INFO, "Initializing raylib %s", RAYLIB_VERSION);
  621. if ((title != NULL) && (title[0] != 0)) CORE.Window.title = title;
  622. // Initialize required global values different than 0
  623. CORE.Input.Keyboard.exitKey = KEY_ESCAPE;
  624. CORE.Input.Mouse.scale = (Vector2){ 1.0f, 1.0f };
  625. CORE.Input.Mouse.cursor = MOUSE_CURSOR_ARROW;
  626. CORE.Input.Gamepad.lastButtonPressed = -1;
  627. #if defined(PLATFORM_ANDROID)
  628. CORE.Window.screen.width = width;
  629. CORE.Window.screen.height = height;
  630. CORE.Window.currentFbo.width = width;
  631. CORE.Window.currentFbo.height = height;
  632. // Set desired windows flags before initializing anything
  633. ANativeActivity_setWindowFlags(CORE.Android.app->activity, AWINDOW_FLAG_FULLSCREEN, 0); //AWINDOW_FLAG_SCALED, AWINDOW_FLAG_DITHER
  634. int orientation = AConfiguration_getOrientation(CORE.Android.app->config);
  635. if (orientation == ACONFIGURATION_ORIENTATION_PORT) TRACELOG(LOG_INFO, "ANDROID: Window orientation set as portrait");
  636. else if (orientation == ACONFIGURATION_ORIENTATION_LAND) TRACELOG(LOG_INFO, "ANDROID: Window orientation set as landscape");
  637. // TODO: Automatic orientation doesn't seem to work
  638. if (width <= height)
  639. {
  640. AConfiguration_setOrientation(CORE.Android.app->config, ACONFIGURATION_ORIENTATION_PORT);
  641. TRACELOG(LOG_WARNING, "ANDROID: Window orientation changed to portrait");
  642. }
  643. else
  644. {
  645. AConfiguration_setOrientation(CORE.Android.app->config, ACONFIGURATION_ORIENTATION_LAND);
  646. TRACELOG(LOG_WARNING, "ANDROID: Window orientation changed to landscape");
  647. }
  648. //AConfiguration_getDensity(CORE.Android.app->config);
  649. //AConfiguration_getKeyboard(CORE.Android.app->config);
  650. //AConfiguration_getScreenSize(CORE.Android.app->config);
  651. //AConfiguration_getScreenLong(CORE.Android.app->config);
  652. // Initialize App command system
  653. // NOTE: On APP_CMD_INIT_WINDOW -> InitGraphicsDevice(), InitTimer(), LoadFontDefault()...
  654. CORE.Android.app->onAppCmd = AndroidCommandCallback;
  655. // Initialize input events system
  656. CORE.Android.app->onInputEvent = AndroidInputCallback;
  657. // Initialize assets manager
  658. InitAssetManager(CORE.Android.app->activity->assetManager, CORE.Android.app->activity->internalDataPath);
  659. // Initialize base path for storage
  660. CORE.Storage.basePath = CORE.Android.app->activity->internalDataPath;
  661. TRACELOG(LOG_INFO, "ANDROID: App initialized successfully");
  662. // Android ALooper_pollAll() variables
  663. int pollResult = 0;
  664. int pollEvents = 0;
  665. // Wait for window to be initialized (display and context)
  666. while (!CORE.Window.ready)
  667. {
  668. // Process events loop
  669. while ((pollResult = ALooper_pollAll(0, NULL, &pollEvents, (void**)&CORE.Android.source)) >= 0)
  670. {
  671. // Process this event
  672. if (CORE.Android.source != NULL) CORE.Android.source->process(CORE.Android.app, CORE.Android.source);
  673. // NOTE: Never close window, native activity is controlled by the system!
  674. //if (CORE.Android.app->destroyRequested != 0) CORE.Window.shouldClose = true;
  675. }
  676. }
  677. #endif
  678. #if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB) || defined(PLATFORM_RPI) || defined(PLATFORM_DRM)
  679. // Initialize graphics device (display device and OpenGL context)
  680. // NOTE: returns true if window and graphic device has been initialized successfully
  681. CORE.Window.ready = InitGraphicsDevice(width, height);
  682. // If graphic device is no properly initialized, we end program
  683. if (!CORE.Window.ready)
  684. {
  685. TRACELOG(LOG_FATAL, "Failed to initialize Graphic Device");
  686. return;
  687. }
  688. // Initialize hi-res timer
  689. InitTimer();
  690. // Initialize random seed
  691. srand((unsigned int)time(NULL));
  692. // Initialize base path for storage
  693. CORE.Storage.basePath = GetWorkingDirectory();
  694. #if defined(SUPPORT_DEFAULT_FONT)
  695. // Load default font
  696. // NOTE: External functions (defined in module: text)
  697. LoadFontDefault();
  698. Rectangle rec = GetFontDefault().recs[95];
  699. // NOTE: We setup a 1px padding on char rectangle to avoid pixel bleeding on MSAA filtering
  700. SetShapesTexture(GetFontDefault().texture, (Rectangle){ rec.x + 1, rec.y + 1, rec.width - 2, rec.height - 2 });
  701. #else
  702. // Set default texture and rectangle to be used for shapes drawing
  703. // NOTE: rlgl default texture is a 1x1 pixel UNCOMPRESSED_R8G8B8A8
  704. Texture2D texture = { rlGetTextureIdDefault(), 1, 1, 1, PIXELFORMAT_UNCOMPRESSED_R8G8B8A8 };
  705. SetShapesTexture(texture, (Rectangle){ 0.0f, 0.0f, 1.0f, 1.0f });
  706. #endif
  707. #if defined(PLATFORM_DESKTOP)
  708. if ((CORE.Window.flags & FLAG_WINDOW_HIGHDPI) > 0)
  709. {
  710. // Set default font texture filter for HighDPI (blurry)
  711. SetTextureFilter(GetFontDefault().texture, TEXTURE_FILTER_BILINEAR);
  712. }
  713. #endif
  714. #if defined(PLATFORM_RPI) || defined(PLATFORM_DRM)
  715. // Initialize raw input system
  716. InitEvdevInput(); // Evdev inputs initialization
  717. InitGamepad(); // Gamepad init
  718. InitKeyboard(); // Keyboard init (stdin)
  719. #endif
  720. #if defined(PLATFORM_WEB)
  721. // Check fullscreen change events(note this is done on the window since most browsers don't support this on #canvas)
  722. //emscripten_set_fullscreenchange_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, NULL, 1, EmscriptenResizeCallback);
  723. // Check Resize event (note this is done on the window since most browsers don't support this on #canvas)
  724. emscripten_set_resize_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, NULL, 1, EmscriptenResizeCallback);
  725. // Trigger this once to get initial window sizing
  726. EmscriptenResizeCallback(EMSCRIPTEN_EVENT_RESIZE, NULL, NULL);
  727. // Support keyboard events
  728. //emscripten_set_keypress_callback("#canvas", NULL, 1, EmscriptenKeyboardCallback);
  729. //emscripten_set_keydown_callback("#canvas", NULL, 1, EmscriptenKeyboardCallback);
  730. // Support mouse events
  731. emscripten_set_click_callback("#canvas", NULL, 1, EmscriptenMouseCallback);
  732. // Support touch events
  733. emscripten_set_touchstart_callback("#canvas", NULL, 1, EmscriptenTouchCallback);
  734. emscripten_set_touchend_callback("#canvas", NULL, 1, EmscriptenTouchCallback);
  735. emscripten_set_touchmove_callback("#canvas", NULL, 1, EmscriptenTouchCallback);
  736. emscripten_set_touchcancel_callback("#canvas", NULL, 1, EmscriptenTouchCallback);
  737. // Support gamepad events (not provided by GLFW3 on emscripten)
  738. emscripten_set_gamepadconnected_callback(NULL, 1, EmscriptenGamepadCallback);
  739. emscripten_set_gamepaddisconnected_callback(NULL, 1, EmscriptenGamepadCallback);
  740. #endif
  741. CORE.Input.Mouse.currentPosition.x = (float)CORE.Window.screen.width/2.0f;
  742. CORE.Input.Mouse.currentPosition.y = (float)CORE.Window.screen.height/2.0f;
  743. #if defined(SUPPORT_EVENTS_AUTOMATION)
  744. events = (AutomationEvent *)malloc(MAX_CODE_AUTOMATION_EVENTS*sizeof(AutomationEvent));
  745. CORE.Time.frameCounter = 0;
  746. #endif
  747. #endif // PLATFORM_DESKTOP || PLATFORM_WEB || PLATFORM_RPI || PLATFORM_DRM
  748. }
  749. // Close window and unload OpenGL context
  750. void CloseWindow(void)
  751. {
  752. #if defined(SUPPORT_GIF_RECORDING)
  753. if (gifRecording)
  754. {
  755. MsfGifResult result = msf_gif_end(&gifState);
  756. msf_gif_free(result);
  757. gifRecording = false;
  758. }
  759. #endif
  760. #if defined(SUPPORT_DEFAULT_FONT)
  761. UnloadFontDefault();
  762. #endif
  763. rlglClose(); // De-init rlgl
  764. #if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB)
  765. glfwDestroyWindow(CORE.Window.handle);
  766. glfwTerminate();
  767. #endif
  768. #if defined(_WIN32) && defined(SUPPORT_WINMM_HIGHRES_TIMER) && !defined(SUPPORT_BUSY_WAIT_LOOP)
  769. timeEndPeriod(1); // Restore time period
  770. #endif
  771. #if defined(PLATFORM_ANDROID) || defined(PLATFORM_RPI)
  772. // Close surface, context and display
  773. if (CORE.Window.device != EGL_NO_DISPLAY)
  774. {
  775. eglMakeCurrent(CORE.Window.device, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
  776. if (CORE.Window.surface != EGL_NO_SURFACE)
  777. {
  778. eglDestroySurface(CORE.Window.device, CORE.Window.surface);
  779. CORE.Window.surface = EGL_NO_SURFACE;
  780. }
  781. if (CORE.Window.context != EGL_NO_CONTEXT)
  782. {
  783. eglDestroyContext(CORE.Window.device, CORE.Window.context);
  784. CORE.Window.context = EGL_NO_CONTEXT;
  785. }
  786. eglTerminate(CORE.Window.device);
  787. CORE.Window.device = EGL_NO_DISPLAY;
  788. }
  789. #endif
  790. #if defined(PLATFORM_DRM)
  791. if (CORE.Window.prevFB)
  792. {
  793. drmModeRmFB(CORE.Window.fd, CORE.Window.prevFB);
  794. CORE.Window.prevFB = 0;
  795. }
  796. if (CORE.Window.prevBO)
  797. {
  798. gbm_surface_release_buffer(CORE.Window.gbmSurface, CORE.Window.prevBO);
  799. CORE.Window.prevBO = NULL;
  800. }
  801. if (CORE.Window.gbmSurface)
  802. {
  803. gbm_surface_destroy(CORE.Window.gbmSurface);
  804. CORE.Window.gbmSurface = NULL;
  805. }
  806. if (CORE.Window.gbmDevice)
  807. {
  808. gbm_device_destroy(CORE.Window.gbmDevice);
  809. CORE.Window.gbmDevice = NULL;
  810. }
  811. if (CORE.Window.crtc)
  812. {
  813. if (CORE.Window.connector)
  814. {
  815. drmModeSetCrtc(CORE.Window.fd, CORE.Window.crtc->crtc_id, CORE.Window.crtc->buffer_id,
  816. CORE.Window.crtc->x, CORE.Window.crtc->y, &CORE.Window.connector->connector_id, 1, &CORE.Window.crtc->mode);
  817. drmModeFreeConnector(CORE.Window.connector);
  818. CORE.Window.connector = NULL;
  819. }
  820. drmModeFreeCrtc(CORE.Window.crtc);
  821. CORE.Window.crtc = NULL;
  822. }
  823. if (CORE.Window.fd != -1)
  824. {
  825. close(CORE.Window.fd);
  826. CORE.Window.fd = -1;
  827. }
  828. // Close surface, context and display
  829. if (CORE.Window.device != EGL_NO_DISPLAY)
  830. {
  831. if (CORE.Window.surface != EGL_NO_SURFACE)
  832. {
  833. eglDestroySurface(CORE.Window.device, CORE.Window.surface);
  834. CORE.Window.surface = EGL_NO_SURFACE;
  835. }
  836. if (CORE.Window.context != EGL_NO_CONTEXT)
  837. {
  838. eglDestroyContext(CORE.Window.device, CORE.Window.context);
  839. CORE.Window.context = EGL_NO_CONTEXT;
  840. }
  841. eglTerminate(CORE.Window.device);
  842. CORE.Window.device = EGL_NO_DISPLAY;
  843. }
  844. #endif
  845. #if defined(PLATFORM_RPI) || defined(PLATFORM_DRM)
  846. // Wait for mouse and gamepad threads to finish before closing
  847. // NOTE: Those threads should already have finished at this point
  848. // because they are controlled by CORE.Window.shouldClose variable
  849. CORE.Window.shouldClose = true; // Added to force threads to exit when the close window is called
  850. // Close the evdev keyboard
  851. if (CORE.Input.Keyboard.fd != -1)
  852. {
  853. close(CORE.Input.Keyboard.fd);
  854. CORE.Input.Keyboard.fd = -1;
  855. }
  856. for (int i = 0; i < sizeof(CORE.Input.eventWorker)/sizeof(InputEventWorker); ++i)
  857. {
  858. if (CORE.Input.eventWorker[i].threadId)
  859. {
  860. pthread_join(CORE.Input.eventWorker[i].threadId, NULL);
  861. }
  862. }
  863. if (CORE.Input.Gamepad.threadId) pthread_join(CORE.Input.Gamepad.threadId, NULL);
  864. #endif
  865. #if defined(SUPPORT_EVENTS_AUTOMATION)
  866. free(events);
  867. #endif
  868. CORE.Window.ready = false;
  869. TRACELOG(LOG_INFO, "Window closed successfully");
  870. }
  871. // Check if KEY_ESCAPE pressed or Close icon pressed
  872. bool WindowShouldClose(void)
  873. {
  874. #if defined(PLATFORM_WEB)
  875. // Emterpreter-Async required to run sync code
  876. // https://github.com/emscripten-core/emscripten/wiki/Emterpreter#emterpreter-async-run-synchronous-code
  877. // By default, this function is never called on a web-ready raylib example because we encapsulate
  878. // frame code in a UpdateDrawFrame() function, to allow browser manage execution asynchronously
  879. // but now emscripten allows sync code to be executed in an interpreted way, using emterpreter!
  880. emscripten_sleep(16);
  881. return false;
  882. #endif
  883. #if defined(PLATFORM_DESKTOP)
  884. if (CORE.Window.ready)
  885. {
  886. // While window minimized, stop loop execution
  887. while (IsWindowState(FLAG_WINDOW_MINIMIZED) && !IsWindowState(FLAG_WINDOW_ALWAYS_RUN)) glfwWaitEvents();
  888. CORE.Window.shouldClose = glfwWindowShouldClose(CORE.Window.handle);
  889. // Reset close status for next frame
  890. glfwSetWindowShouldClose(CORE.Window.handle, GLFW_FALSE);
  891. return CORE.Window.shouldClose;
  892. }
  893. else return true;
  894. #endif
  895. #if defined(PLATFORM_ANDROID) || defined(PLATFORM_RPI) || defined(PLATFORM_DRM)
  896. if (CORE.Window.ready) return CORE.Window.shouldClose;
  897. else return true;
  898. #endif
  899. }
  900. // Check if window has been initialized successfully
  901. bool IsWindowReady(void)
  902. {
  903. return CORE.Window.ready;
  904. }
  905. // Check if window is currently fullscreen
  906. bool IsWindowFullscreen(void)
  907. {
  908. return CORE.Window.fullscreen;
  909. }
  910. // Check if window is currently hidden
  911. bool IsWindowHidden(void)
  912. {
  913. #if defined(PLATFORM_DESKTOP)
  914. return ((CORE.Window.flags & FLAG_WINDOW_HIDDEN) > 0);
  915. #endif
  916. return false;
  917. }
  918. // Check if window has been minimized
  919. bool IsWindowMinimized(void)
  920. {
  921. #if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB)
  922. return ((CORE.Window.flags & FLAG_WINDOW_MINIMIZED) > 0);
  923. #else
  924. return false;
  925. #endif
  926. }
  927. // Check if window has been maximized (only PLATFORM_DESKTOP)
  928. bool IsWindowMaximized(void)
  929. {
  930. #if defined(PLATFORM_DESKTOP)
  931. return ((CORE.Window.flags & FLAG_WINDOW_MAXIMIZED) > 0);
  932. #else
  933. return false;
  934. #endif
  935. }
  936. // Check if window has the focus
  937. bool IsWindowFocused(void)
  938. {
  939. #if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB)
  940. return ((CORE.Window.flags & FLAG_WINDOW_UNFOCUSED) == 0); // TODO!
  941. #else
  942. return true;
  943. #endif
  944. }
  945. // Check if window has been resizedLastFrame
  946. bool IsWindowResized(void)
  947. {
  948. #if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB)
  949. return CORE.Window.resizedLastFrame;
  950. #else
  951. return false;
  952. #endif
  953. }
  954. // Check if one specific window flag is enabled
  955. bool IsWindowState(unsigned int flag)
  956. {
  957. return ((CORE.Window.flags & flag) > 0);
  958. }
  959. // Toggle fullscreen mode (only PLATFORM_DESKTOP)
  960. void ToggleFullscreen(void)
  961. {
  962. #if defined(PLATFORM_DESKTOP)
  963. // NOTE: glfwSetWindowMonitor() doesn't work properly (bugs)
  964. if (!CORE.Window.fullscreen)
  965. {
  966. // Store previous window position (in case we exit fullscreen)
  967. glfwGetWindowPos(CORE.Window.handle, &CORE.Window.position.x, &CORE.Window.position.y);
  968. int monitorCount = 0;
  969. GLFWmonitor** monitors = glfwGetMonitors(&monitorCount);
  970. int monitorIndex = GetCurrentMonitor();
  971. // Use current monitor, so we correctly get the display the window is on
  972. GLFWmonitor* monitor = monitorIndex < monitorCount ? monitors[monitorIndex] : NULL;
  973. if (!monitor)
  974. {
  975. TRACELOG(LOG_WARNING, "GLFW: Failed to get monitor");
  976. CORE.Window.fullscreen = false; // Toggle fullscreen flag
  977. CORE.Window.flags &= ~FLAG_FULLSCREEN_MODE;
  978. glfwSetWindowMonitor(CORE.Window.handle, NULL, 0, 0, CORE.Window.screen.width, CORE.Window.screen.height, GLFW_DONT_CARE);
  979. return;
  980. }
  981. CORE.Window.fullscreen = true; // Toggle fullscreen flag
  982. CORE.Window.flags |= FLAG_FULLSCREEN_MODE;
  983. glfwSetWindowMonitor(CORE.Window.handle, monitor, 0, 0, CORE.Window.screen.width, CORE.Window.screen.height, GLFW_DONT_CARE);
  984. }
  985. else
  986. {
  987. CORE.Window.fullscreen = false; // Toggle fullscreen flag
  988. CORE.Window.flags &= ~FLAG_FULLSCREEN_MODE;
  989. glfwSetWindowMonitor(CORE.Window.handle, NULL, CORE.Window.position.x, CORE.Window.position.y, CORE.Window.screen.width, CORE.Window.screen.height, GLFW_DONT_CARE);
  990. }
  991. // Try to enable GPU V-Sync, so frames are limited to screen refresh rate (60Hz -> 60 FPS)
  992. // NOTE: V-Sync can be enabled by graphic driver configuration
  993. if (CORE.Window.flags & FLAG_VSYNC_HINT) glfwSwapInterval(1);
  994. #endif
  995. #if defined(PLATFORM_WEB)
  996. EM_ASM
  997. (
  998. // This strategy works well while using raylib minimal web shell for emscripten,
  999. // it re-scales the canvas to fullscreen using monitor resolution, for tools this
  1000. // is a good strategy but maybe games prefer to keep current canvas resolution and
  1001. // display it in fullscreen, adjusting monitor resolution if possible
  1002. if (document.fullscreenElement) document.exitFullscreen();
  1003. else Module.requestFullscreen(false, true);
  1004. );
  1005. /*
  1006. if (!CORE.Window.fullscreen)
  1007. {
  1008. // Option 1: Request fullscreen for the canvas element
  1009. // This option does not seem to work at all
  1010. //emscripten_request_fullscreen("#canvas", false);
  1011. // Option 2: Request fullscreen for the canvas element with strategy
  1012. // This option does not seem to work at all
  1013. // Ref: https://github.com/emscripten-core/emscripten/issues/5124
  1014. // EmscriptenFullscreenStrategy strategy = {
  1015. // .scaleMode = EMSCRIPTEN_FULLSCREEN_SCALE_STRETCH, //EMSCRIPTEN_FULLSCREEN_SCALE_ASPECT,
  1016. // .canvasResolutionScaleMode = EMSCRIPTEN_FULLSCREEN_CANVAS_SCALE_STDDEF,
  1017. // .filteringMode = EMSCRIPTEN_FULLSCREEN_FILTERING_DEFAULT,
  1018. // .canvasResizedCallback = EmscriptenWindowResizedCallback,
  1019. // .canvasResizedCallbackUserData = NULL
  1020. // };
  1021. //emscripten_request_fullscreen_strategy("#canvas", EM_FALSE, &strategy);
  1022. // Option 3: Request fullscreen for the canvas element with strategy
  1023. // It works as expected but only inside the browser (client area)
  1024. EmscriptenFullscreenStrategy strategy = {
  1025. .scaleMode = EMSCRIPTEN_FULLSCREEN_SCALE_ASPECT,
  1026. .canvasResolutionScaleMode = EMSCRIPTEN_FULLSCREEN_CANVAS_SCALE_STDDEF,
  1027. .filteringMode = EMSCRIPTEN_FULLSCREEN_FILTERING_DEFAULT,
  1028. .canvasResizedCallback = EmscriptenWindowResizedCallback,
  1029. .canvasResizedCallbackUserData = NULL
  1030. };
  1031. emscripten_enter_soft_fullscreen("#canvas", &strategy);
  1032. int width, height;
  1033. emscripten_get_canvas_element_size("#canvas", &width, &height);
  1034. TRACELOG(LOG_WARNING, "Emscripten: Enter fullscreen: Canvas size: %i x %i", width, height);
  1035. }
  1036. else
  1037. {
  1038. //emscripten_exit_fullscreen();
  1039. emscripten_exit_soft_fullscreen();
  1040. int width, height;
  1041. emscripten_get_canvas_element_size("#canvas", &width, &height);
  1042. TRACELOG(LOG_WARNING, "Emscripten: Exit fullscreen: Canvas size: %i x %i", width, height);
  1043. }
  1044. */
  1045. CORE.Window.fullscreen = !CORE.Window.fullscreen; // Toggle fullscreen flag
  1046. CORE.Window.flags ^= FLAG_FULLSCREEN_MODE;
  1047. #endif
  1048. #if defined(PLATFORM_ANDROID) || defined(PLATFORM_RPI) || defined(PLATFORM_DRM)
  1049. TRACELOG(LOG_WARNING, "SYSTEM: Failed to toggle to windowed mode");
  1050. #endif
  1051. }
  1052. // Set window state: maximized, if resizable (only PLATFORM_DESKTOP)
  1053. void MaximizeWindow(void)
  1054. {
  1055. #if defined(PLATFORM_DESKTOP)
  1056. if (glfwGetWindowAttrib(CORE.Window.handle, GLFW_RESIZABLE) == GLFW_TRUE)
  1057. {
  1058. glfwMaximizeWindow(CORE.Window.handle);
  1059. CORE.Window.flags |= FLAG_WINDOW_MAXIMIZED;
  1060. }
  1061. #endif
  1062. }
  1063. // Set window state: minimized (only PLATFORM_DESKTOP)
  1064. void MinimizeWindow(void)
  1065. {
  1066. #if defined(PLATFORM_DESKTOP)
  1067. // NOTE: Following function launches callback that sets appropiate flag!
  1068. glfwIconifyWindow(CORE.Window.handle);
  1069. #endif
  1070. }
  1071. // Set window state: not minimized/maximized (only PLATFORM_DESKTOP)
  1072. void RestoreWindow(void)
  1073. {
  1074. #if defined(PLATFORM_DESKTOP)
  1075. if (glfwGetWindowAttrib(CORE.Window.handle, GLFW_RESIZABLE) == GLFW_TRUE)
  1076. {
  1077. // Restores the specified window if it was previously iconified (minimized) or maximized
  1078. glfwRestoreWindow(CORE.Window.handle);
  1079. CORE.Window.flags &= ~FLAG_WINDOW_MINIMIZED;
  1080. CORE.Window.flags &= ~FLAG_WINDOW_MAXIMIZED;
  1081. }
  1082. #endif
  1083. }
  1084. // Set window configuration state using flags
  1085. void SetWindowState(unsigned int flags)
  1086. {
  1087. #if defined(PLATFORM_DESKTOP)
  1088. // Check previous state and requested state to apply required changes
  1089. // NOTE: In most cases the functions already change the flags internally
  1090. // State change: FLAG_VSYNC_HINT
  1091. if (((CORE.Window.flags & FLAG_VSYNC_HINT) != (flags & FLAG_VSYNC_HINT)) && ((flags & FLAG_VSYNC_HINT) > 0))
  1092. {
  1093. glfwSwapInterval(1);
  1094. CORE.Window.flags |= FLAG_VSYNC_HINT;
  1095. }
  1096. // State change: FLAG_FULLSCREEN_MODE
  1097. if ((CORE.Window.flags & FLAG_FULLSCREEN_MODE) != (flags & FLAG_FULLSCREEN_MODE))
  1098. {
  1099. ToggleFullscreen(); // NOTE: Window state flag updated inside function
  1100. }
  1101. // State change: FLAG_WINDOW_RESIZABLE
  1102. if (((CORE.Window.flags & FLAG_WINDOW_RESIZABLE) != (flags & FLAG_WINDOW_RESIZABLE)) && ((flags & FLAG_WINDOW_RESIZABLE) > 0))
  1103. {
  1104. glfwSetWindowAttrib(CORE.Window.handle, GLFW_RESIZABLE, GLFW_TRUE);
  1105. CORE.Window.flags |= FLAG_WINDOW_RESIZABLE;
  1106. }
  1107. // State change: FLAG_WINDOW_UNDECORATED
  1108. if (((CORE.Window.flags & FLAG_WINDOW_UNDECORATED) != (flags & FLAG_WINDOW_UNDECORATED)) && (flags & FLAG_WINDOW_UNDECORATED))
  1109. {
  1110. glfwSetWindowAttrib(CORE.Window.handle, GLFW_DECORATED, GLFW_FALSE);
  1111. CORE.Window.flags |= FLAG_WINDOW_UNDECORATED;
  1112. }
  1113. // State change: FLAG_WINDOW_HIDDEN
  1114. if (((CORE.Window.flags & FLAG_WINDOW_HIDDEN) != (flags & FLAG_WINDOW_HIDDEN)) && ((flags & FLAG_WINDOW_HIDDEN) > 0))
  1115. {
  1116. glfwHideWindow(CORE.Window.handle);
  1117. CORE.Window.flags |= FLAG_WINDOW_HIDDEN;
  1118. }
  1119. // State change: FLAG_WINDOW_MINIMIZED
  1120. if (((CORE.Window.flags & FLAG_WINDOW_MINIMIZED) != (flags & FLAG_WINDOW_MINIMIZED)) && ((flags & FLAG_WINDOW_MINIMIZED) > 0))
  1121. {
  1122. //GLFW_ICONIFIED
  1123. MinimizeWindow(); // NOTE: Window state flag updated inside function
  1124. }
  1125. // State change: FLAG_WINDOW_MAXIMIZED
  1126. if (((CORE.Window.flags & FLAG_WINDOW_MAXIMIZED) != (flags & FLAG_WINDOW_MAXIMIZED)) && ((flags & FLAG_WINDOW_MAXIMIZED) > 0))
  1127. {
  1128. //GLFW_MAXIMIZED
  1129. MaximizeWindow(); // NOTE: Window state flag updated inside function
  1130. }
  1131. // State change: FLAG_WINDOW_UNFOCUSED
  1132. if (((CORE.Window.flags & FLAG_WINDOW_UNFOCUSED) != (flags & FLAG_WINDOW_UNFOCUSED)) && ((flags & FLAG_WINDOW_UNFOCUSED) > 0))
  1133. {
  1134. glfwSetWindowAttrib(CORE.Window.handle, GLFW_FOCUS_ON_SHOW, GLFW_FALSE);
  1135. CORE.Window.flags |= FLAG_WINDOW_UNFOCUSED;
  1136. }
  1137. // State change: FLAG_WINDOW_TOPMOST
  1138. if (((CORE.Window.flags & FLAG_WINDOW_TOPMOST) != (flags & FLAG_WINDOW_TOPMOST)) && ((flags & FLAG_WINDOW_TOPMOST) > 0))
  1139. {
  1140. glfwSetWindowAttrib(CORE.Window.handle, GLFW_FLOATING, GLFW_TRUE);
  1141. CORE.Window.flags |= FLAG_WINDOW_TOPMOST;
  1142. }
  1143. // State change: FLAG_WINDOW_ALWAYS_RUN
  1144. if (((CORE.Window.flags & FLAG_WINDOW_ALWAYS_RUN) != (flags & FLAG_WINDOW_ALWAYS_RUN)) && ((flags & FLAG_WINDOW_ALWAYS_RUN) > 0))
  1145. {
  1146. CORE.Window.flags |= FLAG_WINDOW_ALWAYS_RUN;
  1147. }
  1148. // The following states can not be changed after window creation
  1149. // State change: FLAG_WINDOW_TRANSPARENT
  1150. if (((CORE.Window.flags & FLAG_WINDOW_TRANSPARENT) != (flags & FLAG_WINDOW_TRANSPARENT)) && ((flags & FLAG_WINDOW_TRANSPARENT) > 0))
  1151. {
  1152. TRACELOG(LOG_WARNING, "WINDOW: Framebuffer transparency can only by configured before window initialization");
  1153. }
  1154. // State change: FLAG_WINDOW_HIGHDPI
  1155. if (((CORE.Window.flags & FLAG_WINDOW_HIGHDPI) != (flags & FLAG_WINDOW_HIGHDPI)) && ((flags & FLAG_WINDOW_HIGHDPI) > 0))
  1156. {
  1157. TRACELOG(LOG_WARNING, "WINDOW: High DPI can only by configured before window initialization");
  1158. }
  1159. // State change: FLAG_MSAA_4X_HINT
  1160. if (((CORE.Window.flags & FLAG_MSAA_4X_HINT) != (flags & FLAG_MSAA_4X_HINT)) && ((flags & FLAG_MSAA_4X_HINT) > 0))
  1161. {
  1162. TRACELOG(LOG_WARNING, "WINDOW: MSAA can only by configured before window initialization");
  1163. }
  1164. // State change: FLAG_INTERLACED_HINT
  1165. if (((CORE.Window.flags & FLAG_INTERLACED_HINT) != (flags & FLAG_INTERLACED_HINT)) && ((flags & FLAG_INTERLACED_HINT) > 0))
  1166. {
  1167. TRACELOG(LOG_WARNING, "RPI: Interlaced mode can only by configured before window initialization");
  1168. }
  1169. #endif
  1170. }
  1171. // Clear window configuration state flags
  1172. void ClearWindowState(unsigned int flags)
  1173. {
  1174. #if defined(PLATFORM_DESKTOP)
  1175. // Check previous state and requested state to apply required changes
  1176. // NOTE: In most cases the functions already change the flags internally
  1177. // State change: FLAG_VSYNC_HINT
  1178. if (((CORE.Window.flags & FLAG_VSYNC_HINT) > 0) && ((flags & FLAG_VSYNC_HINT) > 0))
  1179. {
  1180. glfwSwapInterval(0);
  1181. CORE.Window.flags &= ~FLAG_VSYNC_HINT;
  1182. }
  1183. // State change: FLAG_FULLSCREEN_MODE
  1184. if (((CORE.Window.flags & FLAG_FULLSCREEN_MODE) > 0) && ((flags & FLAG_FULLSCREEN_MODE) > 0))
  1185. {
  1186. ToggleFullscreen(); // NOTE: Window state flag updated inside function
  1187. }
  1188. // State change: FLAG_WINDOW_RESIZABLE
  1189. if (((CORE.Window.flags & FLAG_WINDOW_RESIZABLE) > 0) && ((flags & FLAG_WINDOW_RESIZABLE) > 0))
  1190. {
  1191. glfwSetWindowAttrib(CORE.Window.handle, GLFW_RESIZABLE, GLFW_FALSE);
  1192. CORE.Window.flags &= ~FLAG_WINDOW_RESIZABLE;
  1193. }
  1194. // State change: FLAG_WINDOW_UNDECORATED
  1195. if (((CORE.Window.flags & FLAG_WINDOW_UNDECORATED) > 0) && ((flags & FLAG_WINDOW_UNDECORATED) > 0))
  1196. {
  1197. glfwSetWindowAttrib(CORE.Window.handle, GLFW_DECORATED, GLFW_TRUE);
  1198. CORE.Window.flags &= ~FLAG_WINDOW_UNDECORATED;
  1199. }
  1200. // State change: FLAG_WINDOW_HIDDEN
  1201. if (((CORE.Window.flags & FLAG_WINDOW_HIDDEN) > 0) && ((flags & FLAG_WINDOW_HIDDEN) > 0))
  1202. {
  1203. glfwShowWindow(CORE.Window.handle);
  1204. CORE.Window.flags &= ~FLAG_WINDOW_HIDDEN;
  1205. }
  1206. // State change: FLAG_WINDOW_MINIMIZED
  1207. if (((CORE.Window.flags & FLAG_WINDOW_MINIMIZED) > 0) && ((flags & FLAG_WINDOW_MINIMIZED) > 0))
  1208. {
  1209. RestoreWindow(); // NOTE: Window state flag updated inside function
  1210. }
  1211. // State change: FLAG_WINDOW_MAXIMIZED
  1212. if (((CORE.Window.flags & FLAG_WINDOW_MAXIMIZED) > 0) && ((flags & FLAG_WINDOW_MAXIMIZED) > 0))
  1213. {
  1214. RestoreWindow(); // NOTE: Window state flag updated inside function
  1215. }
  1216. // State change: FLAG_WINDOW_UNFOCUSED
  1217. if (((CORE.Window.flags & FLAG_WINDOW_UNFOCUSED) > 0) && ((flags & FLAG_WINDOW_UNFOCUSED) > 0))
  1218. {
  1219. glfwSetWindowAttrib(CORE.Window.handle, GLFW_FOCUS_ON_SHOW, GLFW_TRUE);
  1220. CORE.Window.flags &= ~FLAG_WINDOW_UNFOCUSED;
  1221. }
  1222. // State change: FLAG_WINDOW_TOPMOST
  1223. if (((CORE.Window.flags & FLAG_WINDOW_TOPMOST) > 0) && ((flags & FLAG_WINDOW_TOPMOST) > 0))
  1224. {
  1225. glfwSetWindowAttrib(CORE.Window.handle, GLFW_FLOATING, GLFW_FALSE);
  1226. CORE.Window.flags &= ~FLAG_WINDOW_TOPMOST;
  1227. }
  1228. // State change: FLAG_WINDOW_ALWAYS_RUN
  1229. if (((CORE.Window.flags & FLAG_WINDOW_ALWAYS_RUN) > 0) && ((flags & FLAG_WINDOW_ALWAYS_RUN) > 0))
  1230. {
  1231. CORE.Window.flags &= ~FLAG_WINDOW_ALWAYS_RUN;
  1232. }
  1233. // The following states can not be changed after window creation
  1234. // State change: FLAG_WINDOW_TRANSPARENT
  1235. if (((CORE.Window.flags & FLAG_WINDOW_TRANSPARENT) > 0) && ((flags & FLAG_WINDOW_TRANSPARENT) > 0))
  1236. {
  1237. TRACELOG(LOG_WARNING, "WINDOW: Framebuffer transparency can only by configured before window initialization");
  1238. }
  1239. // State change: FLAG_WINDOW_HIGHDPI
  1240. if (((CORE.Window.flags & FLAG_WINDOW_HIGHDPI) > 0) && ((flags & FLAG_WINDOW_HIGHDPI) > 0))
  1241. {
  1242. TRACELOG(LOG_WARNING, "WINDOW: High DPI can only by configured before window initialization");
  1243. }
  1244. // State change: FLAG_MSAA_4X_HINT
  1245. if (((CORE.Window.flags & FLAG_MSAA_4X_HINT) > 0) && ((flags & FLAG_MSAA_4X_HINT) > 0))
  1246. {
  1247. TRACELOG(LOG_WARNING, "WINDOW: MSAA can only by configured before window initialization");
  1248. }
  1249. // State change: FLAG_INTERLACED_HINT
  1250. if (((CORE.Window.flags & FLAG_INTERLACED_HINT) > 0) && ((flags & FLAG_INTERLACED_HINT) > 0))
  1251. {
  1252. TRACELOG(LOG_WARNING, "RPI: Interlaced mode can only by configured before window initialization");
  1253. }
  1254. #endif
  1255. }
  1256. // Set icon for window (only PLATFORM_DESKTOP)
  1257. // NOTE: Image must be in RGBA format, 8bit per channel
  1258. void SetWindowIcon(Image image)
  1259. {
  1260. #if defined(PLATFORM_DESKTOP)
  1261. if (image.format == PIXELFORMAT_UNCOMPRESSED_R8G8B8A8)
  1262. {
  1263. GLFWimage icon[1] = { 0 };
  1264. icon[0].width = image.width;
  1265. icon[0].height = image.height;
  1266. icon[0].pixels = (unsigned char *)image.data;
  1267. // NOTE 1: We only support one image icon
  1268. // NOTE 2: The specified image data is copied before this function returns
  1269. glfwSetWindowIcon(CORE.Window.handle, 1, icon);
  1270. }
  1271. else TRACELOG(LOG_WARNING, "GLFW: Window icon image must be in R8G8B8A8 pixel format");
  1272. #endif
  1273. }
  1274. // Set title for window (only PLATFORM_DESKTOP)
  1275. void SetWindowTitle(const char *title)
  1276. {
  1277. CORE.Window.title = title;
  1278. #if defined(PLATFORM_DESKTOP)
  1279. glfwSetWindowTitle(CORE.Window.handle, title);
  1280. #endif
  1281. }
  1282. // Set window position on screen (windowed mode)
  1283. void SetWindowPosition(int x, int y)
  1284. {
  1285. #if defined(PLATFORM_DESKTOP)
  1286. glfwSetWindowPos(CORE.Window.handle, x, y);
  1287. #endif
  1288. }
  1289. // Set monitor for the current window (fullscreen mode)
  1290. void SetWindowMonitor(int monitor)
  1291. {
  1292. #if defined(PLATFORM_DESKTOP)
  1293. int monitorCount = 0;
  1294. GLFWmonitor **monitors = glfwGetMonitors(&monitorCount);
  1295. if ((monitor >= 0) && (monitor < monitorCount))
  1296. {
  1297. TRACELOG(LOG_INFO, "GLFW: Selected fullscreen monitor: [%i] %s", monitor, glfwGetMonitorName(monitors[monitor]));
  1298. const GLFWvidmode *mode = glfwGetVideoMode(monitors[monitor]);
  1299. glfwSetWindowMonitor(CORE.Window.handle, monitors[monitor], 0, 0, mode->width, mode->height, mode->refreshRate);
  1300. }
  1301. else TRACELOG(LOG_WARNING, "GLFW: Failed to find selected monitor");
  1302. #endif
  1303. }
  1304. // Set window minimum dimensions (FLAG_WINDOW_RESIZABLE)
  1305. void SetWindowMinSize(int width, int height)
  1306. {
  1307. #if defined(PLATFORM_DESKTOP)
  1308. const GLFWvidmode *mode = glfwGetVideoMode(glfwGetPrimaryMonitor());
  1309. glfwSetWindowSizeLimits(CORE.Window.handle, width, height, mode->width, mode->height);
  1310. #endif
  1311. }
  1312. // Set window dimensions
  1313. // TODO: Issues on HighDPI scaling
  1314. void SetWindowSize(int width, int height)
  1315. {
  1316. #if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB)
  1317. glfwSetWindowSize(CORE.Window.handle, width, height);
  1318. #endif
  1319. #if defined(PLATFORM_WEB)
  1320. //emscripten_set_canvas_size(width, height); // DEPRECATED!
  1321. // TODO: Below functions should be used to replace previous one but
  1322. // they do not seem to work properly
  1323. //emscripten_set_canvas_element_size("canvas", width, height);
  1324. //emscripten_set_element_css_size("canvas", width, height);
  1325. #endif
  1326. }
  1327. // Get current screen width
  1328. int GetScreenWidth(void)
  1329. {
  1330. return CORE.Window.currentFbo.width;
  1331. }
  1332. // Get current screen height
  1333. int GetScreenHeight(void)
  1334. {
  1335. return CORE.Window.currentFbo.height;
  1336. }
  1337. // Get native window handle
  1338. void *GetWindowHandle(void)
  1339. {
  1340. #if defined(PLATFORM_DESKTOP) && defined(_WIN32)
  1341. // NOTE: Returned handle is: void *HWND (windows.h)
  1342. return glfwGetWin32Window(CORE.Window.handle);
  1343. #endif
  1344. #if defined(__linux__)
  1345. // NOTE: Returned handle is: unsigned long Window (X.h)
  1346. // typedef unsigned long XID;
  1347. // typedef XID Window;
  1348. //unsigned long id = (unsigned long)glfwGetX11Window(window);
  1349. return NULL; // TODO: Find a way to return value... cast to void *?
  1350. #endif
  1351. #if defined(__APPLE__)
  1352. // NOTE: Returned handle is: (objc_object *)
  1353. return NULL; // TODO: return (void *)glfwGetCocoaWindow(window);
  1354. #endif
  1355. return NULL;
  1356. }
  1357. // Get number of monitors
  1358. int GetMonitorCount(void)
  1359. {
  1360. #if defined(PLATFORM_DESKTOP)
  1361. int monitorCount;
  1362. glfwGetMonitors(&monitorCount);
  1363. return monitorCount;
  1364. #else
  1365. return 1;
  1366. #endif
  1367. }
  1368. // Get number of monitors
  1369. int GetCurrentMonitor(void)
  1370. {
  1371. #if defined(PLATFORM_DESKTOP)
  1372. int monitorCount;
  1373. GLFWmonitor** monitors = glfwGetMonitors(&monitorCount);
  1374. GLFWmonitor* monitor = NULL;
  1375. if (monitorCount == 1) // easy out
  1376. return 0;
  1377. if (IsWindowFullscreen())
  1378. {
  1379. monitor = glfwGetWindowMonitor(CORE.Window.handle);
  1380. for (int i = 0; i < monitorCount; i++)
  1381. {
  1382. if (monitors[i] == monitor)
  1383. return i;
  1384. }
  1385. return 0;
  1386. }
  1387. else
  1388. {
  1389. int x = 0;
  1390. int y = 0;
  1391. glfwGetWindowPos(CORE.Window.handle, &x, &y);
  1392. for (int i = 0; i < monitorCount; i++)
  1393. {
  1394. int mx = 0;
  1395. int my = 0;
  1396. int width = 0;
  1397. int height = 0;
  1398. monitor = monitors[i];
  1399. glfwGetMonitorWorkarea(monitor, &mx, &my, &width, &height);
  1400. if (x >= mx && x <= (mx + width) && y >= my && y <= (my + height))
  1401. return i;
  1402. }
  1403. }
  1404. return 0;
  1405. #else
  1406. return 0;
  1407. #endif
  1408. }
  1409. // Get selected monitor width
  1410. Vector2 GetMonitorPosition(int monitor)
  1411. {
  1412. #if defined(PLATFORM_DESKTOP)
  1413. int monitorCount;
  1414. GLFWmonitor** monitors = glfwGetMonitors(&monitorCount);
  1415. if ((monitor >= 0) && (monitor < monitorCount))
  1416. {
  1417. int x, y;
  1418. glfwGetMonitorPos(monitors[monitor], &x, &y);
  1419. return (Vector2){ (float)x, (float)y };
  1420. }
  1421. else TRACELOG(LOG_WARNING, "GLFW: Failed to find selected monitor");
  1422. #endif
  1423. return (Vector2){ 0, 0 };
  1424. }
  1425. // Get selected monitor width (max available by monitor)
  1426. int GetMonitorWidth(int monitor)
  1427. {
  1428. #if defined(PLATFORM_DESKTOP)
  1429. int monitorCount;
  1430. GLFWmonitor **monitors = glfwGetMonitors(&monitorCount);
  1431. if ((monitor >= 0) && (monitor < monitorCount))
  1432. {
  1433. int count = 0;
  1434. const GLFWvidmode *modes = glfwGetVideoModes(monitors[monitor], &count);
  1435. // We return the maximum resolution available, the last one in the modes array
  1436. if (count > 0) return modes[count - 1].width;
  1437. else TRACELOG(LOG_WARNING, "GLFW: Failed to find video mode for selected monitor");
  1438. }
  1439. else TRACELOG(LOG_WARNING, "GLFW: Failed to find selected monitor");
  1440. #endif
  1441. return 0;
  1442. }
  1443. // Get selected monitor width (max available by monitor)
  1444. int GetMonitorHeight(int monitor)
  1445. {
  1446. #if defined(PLATFORM_DESKTOP)
  1447. int monitorCount;
  1448. GLFWmonitor **monitors = glfwGetMonitors(&monitorCount);
  1449. if ((monitor >= 0) && (monitor < monitorCount))
  1450. {
  1451. int count = 0;
  1452. const GLFWvidmode *modes = glfwGetVideoModes(monitors[monitor], &count);
  1453. // We return the maximum resolution available, the last one in the modes array
  1454. if (count > 0) return modes[count - 1].height;
  1455. else TRACELOG(LOG_WARNING, "GLFW: Failed to find video mode for selected monitor");
  1456. }
  1457. else TRACELOG(LOG_WARNING, "GLFW: Failed to find selected monitor");
  1458. #endif
  1459. return 0;
  1460. }
  1461. // Get selected monitor physical width in millimetres
  1462. int GetMonitorPhysicalWidth(int monitor)
  1463. {
  1464. #if defined(PLATFORM_DESKTOP)
  1465. int monitorCount;
  1466. GLFWmonitor **monitors = glfwGetMonitors(&monitorCount);
  1467. if ((monitor >= 0) && (monitor < monitorCount))
  1468. {
  1469. int physicalWidth;
  1470. glfwGetMonitorPhysicalSize(monitors[monitor], &physicalWidth, NULL);
  1471. return physicalWidth;
  1472. }
  1473. else TRACELOG(LOG_WARNING, "GLFW: Failed to find selected monitor");
  1474. #endif
  1475. return 0;
  1476. }
  1477. // Get primary monitor physical height in millimetres
  1478. int GetMonitorPhysicalHeight(int monitor)
  1479. {
  1480. #if defined(PLATFORM_DESKTOP)
  1481. int monitorCount;
  1482. GLFWmonitor **monitors = glfwGetMonitors(&monitorCount);
  1483. if ((monitor >= 0) && (monitor < monitorCount))
  1484. {
  1485. int physicalHeight;
  1486. glfwGetMonitorPhysicalSize(monitors[monitor], NULL, &physicalHeight);
  1487. return physicalHeight;
  1488. }
  1489. else TRACELOG(LOG_WARNING, "GLFW: Failed to find selected monitor");
  1490. #endif
  1491. return 0;
  1492. }
  1493. int GetMonitorRefreshRate(int monitor)
  1494. {
  1495. #if defined(PLATFORM_DESKTOP)
  1496. int monitorCount;
  1497. GLFWmonitor **monitors = glfwGetMonitors(&monitorCount);
  1498. if ((monitor >= 0) && (monitor < monitorCount))
  1499. {
  1500. const GLFWvidmode *vidmode = glfwGetVideoMode(monitors[monitor]);
  1501. return vidmode->refreshRate;
  1502. }
  1503. else TRACELOG(LOG_WARNING, "GLFW: Failed to find selected monitor");
  1504. #endif
  1505. #if defined(PLATFORM_DRM)
  1506. if ((CORE.Window.connector) && (CORE.Window.modeIndex >= 0))
  1507. {
  1508. return CORE.Window.connector->modes[CORE.Window.modeIndex].vrefresh;
  1509. }
  1510. #endif
  1511. return 0;
  1512. }
  1513. // Get window position XY on monitor
  1514. Vector2 GetWindowPosition(void)
  1515. {
  1516. int x = 0;
  1517. int y = 0;
  1518. #if defined(PLATFORM_DESKTOP)
  1519. glfwGetWindowPos(CORE.Window.handle, &x, &y);
  1520. #endif
  1521. return (Vector2){ (float)x, (float)y };
  1522. }
  1523. // Get window scale DPI factor
  1524. Vector2 GetWindowScaleDPI(void)
  1525. {
  1526. Vector2 scale = { 1.0f, 1.0f };
  1527. #if defined(PLATFORM_DESKTOP)
  1528. float xdpi = 1.0;
  1529. float ydpi = 1.0;
  1530. Vector2 windowPos = GetWindowPosition();
  1531. int monitorCount = 0;
  1532. GLFWmonitor **monitors = glfwGetMonitors(&monitorCount);
  1533. // Check window monitor
  1534. for (int i = 0; i < monitorCount; i++)
  1535. {
  1536. glfwGetMonitorContentScale(monitors[i], &xdpi, &ydpi);
  1537. int xpos, ypos, width, height;
  1538. glfwGetMonitorWorkarea(monitors[i], &xpos, &ypos, &width, &height);
  1539. if ((windowPos.x >= xpos) && (windowPos.x < xpos + width) &&
  1540. (windowPos.y >= ypos) && (windowPos.y < ypos + height))
  1541. {
  1542. scale.x = xdpi;
  1543. scale.y = ydpi;
  1544. break;
  1545. }
  1546. }
  1547. #endif
  1548. return scale;
  1549. }
  1550. // Get the human-readable, UTF-8 encoded name of the primary monitor
  1551. const char *GetMonitorName(int monitor)
  1552. {
  1553. #if defined(PLATFORM_DESKTOP)
  1554. int monitorCount;
  1555. GLFWmonitor **monitors = glfwGetMonitors(&monitorCount);
  1556. if ((monitor >= 0) && (monitor < monitorCount))
  1557. {
  1558. return glfwGetMonitorName(monitors[monitor]);
  1559. }
  1560. else TRACELOG(LOG_WARNING, "GLFW: Failed to find selected monitor");
  1561. #endif
  1562. return "";
  1563. }
  1564. // Get clipboard text content
  1565. // NOTE: returned string is allocated and freed by GLFW
  1566. const char *GetClipboardText(void)
  1567. {
  1568. #if defined(PLATFORM_DESKTOP)
  1569. return glfwGetClipboardString(CORE.Window.handle);
  1570. #else
  1571. return NULL;
  1572. #endif
  1573. }
  1574. // Set clipboard text content
  1575. void SetClipboardText(const char *text)
  1576. {
  1577. #if defined(PLATFORM_DESKTOP)
  1578. glfwSetClipboardString(CORE.Window.handle, text);
  1579. #endif
  1580. }
  1581. // Show mouse cursor
  1582. void ShowCursor(void)
  1583. {
  1584. #if defined(PLATFORM_DESKTOP)
  1585. glfwSetInputMode(CORE.Window.handle, GLFW_CURSOR, GLFW_CURSOR_NORMAL);
  1586. #endif
  1587. CORE.Input.Mouse.cursorHidden = false;
  1588. }
  1589. // Hides mouse cursor
  1590. void HideCursor(void)
  1591. {
  1592. #if defined(PLATFORM_DESKTOP)
  1593. glfwSetInputMode(CORE.Window.handle, GLFW_CURSOR, GLFW_CURSOR_HIDDEN);
  1594. #endif
  1595. CORE.Input.Mouse.cursorHidden = true;
  1596. }
  1597. // Check if cursor is not visible
  1598. bool IsCursorHidden(void)
  1599. {
  1600. return CORE.Input.Mouse.cursorHidden;
  1601. }
  1602. // Enables cursor (unlock cursor)
  1603. void EnableCursor(void)
  1604. {
  1605. #if defined(PLATFORM_DESKTOP)
  1606. glfwSetInputMode(CORE.Window.handle, GLFW_CURSOR, GLFW_CURSOR_NORMAL);
  1607. #endif
  1608. #if defined(PLATFORM_WEB)
  1609. emscripten_exit_pointerlock();
  1610. #endif
  1611. CORE.Input.Mouse.cursorHidden = false;
  1612. }
  1613. // Disables cursor (lock cursor)
  1614. void DisableCursor(void)
  1615. {
  1616. #if defined(PLATFORM_DESKTOP)
  1617. glfwSetInputMode(CORE.Window.handle, GLFW_CURSOR, GLFW_CURSOR_DISABLED);
  1618. #endif
  1619. #if defined(PLATFORM_WEB)
  1620. emscripten_request_pointerlock("#canvas", 1);
  1621. #endif
  1622. CORE.Input.Mouse.cursorHidden = true;
  1623. }
  1624. // Check if cursor is on the current screen.
  1625. bool IsCursorOnScreen(void)
  1626. {
  1627. return CORE.Input.Mouse.cursorOnScreen;
  1628. }
  1629. // Set background color (framebuffer clear color)
  1630. void ClearBackground(Color color)
  1631. {
  1632. rlClearColor(color.r, color.g, color.b, color.a); // Set clear color
  1633. rlClearScreenBuffers(); // Clear current framebuffers
  1634. }
  1635. // Setup canvas (framebuffer) to start drawing
  1636. void BeginDrawing(void)
  1637. {
  1638. // WARNING: Previously to BeginDrawing() other render textures drawing could happen,
  1639. // consequently the measure for update vs draw is not accurate (only the total frame time is accurate)
  1640. CORE.Time.current = GetTime(); // Number of elapsed seconds since InitTimer()
  1641. CORE.Time.update = CORE.Time.current - CORE.Time.previous;
  1642. CORE.Time.previous = CORE.Time.current;
  1643. rlLoadIdentity(); // Reset current matrix (modelview)
  1644. rlMultMatrixf(MatrixToFloat(CORE.Window.screenScale)); // Apply screen scaling
  1645. //rlTranslatef(0.375, 0.375, 0); // HACK to have 2D pixel-perfect drawing on OpenGL 1.1
  1646. // NOTE: Not required with OpenGL 3.3+
  1647. }
  1648. // End canvas drawing and swap buffers (double buffering)
  1649. void EndDrawing(void)
  1650. {
  1651. rlDrawRenderBatchActive(); // Update and draw internal render batch
  1652. #if defined(SUPPORT_MOUSE_CURSOR_POINT)
  1653. // Draw a small rectangle on mouse position for user reference
  1654. if (!CORE.Input.Mouse.cursorHidden)
  1655. {
  1656. DrawRectangle(CORE.Input.Mouse.currentPosition.x, CORE.Input.Mouse.currentPosition.y, 3, 3, MAROON);
  1657. rlDrawRenderBatchActive(); // Update and draw internal render batch
  1658. }
  1659. #endif
  1660. #if defined(SUPPORT_GIF_RECORDING)
  1661. // Draw record indicator
  1662. if (gifRecording)
  1663. {
  1664. #define GIF_RECORD_FRAMERATE 10
  1665. gifFrameCounter++;
  1666. // NOTE: We record one gif frame every 10 game frames
  1667. if ((gifFrameCounter%GIF_RECORD_FRAMERATE) == 0)
  1668. {
  1669. // Get image data for the current frame (from backbuffer)
  1670. // NOTE: This process is quite slow... :(
  1671. unsigned char *screenData = rlReadScreenPixels(CORE.Window.screen.width, CORE.Window.screen.height);
  1672. msf_gif_frame(&gifState, screenData, 10, 16, CORE.Window.screen.width*4);
  1673. RL_FREE(screenData); // Free image data
  1674. }
  1675. if (((gifFrameCounter/15)%2) == 1)
  1676. {
  1677. DrawCircle(30, CORE.Window.screen.height - 20, 10, MAROON);
  1678. DrawText("GIF RECORDING", 50, CORE.Window.screen.height - 25, 10, RED);
  1679. }
  1680. rlDrawRenderBatchActive(); // Update and draw internal render batch
  1681. }
  1682. #endif
  1683. #if defined(SUPPORT_EVENTS_AUTOMATION)
  1684. // Draw record/play indicator
  1685. if (eventsRecording)
  1686. {
  1687. gifFrameCounter++;
  1688. if (((gifFrameCounter/15)%2) == 1)
  1689. {
  1690. DrawCircle(30, CORE.Window.screen.height - 20, 10, MAROON);
  1691. DrawText("EVENTS RECORDING", 50, CORE.Window.screen.height - 25, 10, RED);
  1692. }
  1693. rlDrawRenderBatchActive(); // Update and draw internal render batch
  1694. }
  1695. else if (eventsPlaying)
  1696. {
  1697. gifFrameCounter++;
  1698. if (((gifFrameCounter/15)%2) == 1)
  1699. {
  1700. DrawCircle(30, CORE.Window.screen.height - 20, 10, LIME);
  1701. DrawText("EVENTS PLAYING", 50, CORE.Window.screen.height - 25, 10, GREEN);
  1702. }
  1703. rlDrawRenderBatchActive(); // Update and draw internal render batch
  1704. }
  1705. #endif
  1706. #if !defined(SUPPORT_CUSTOM_FRAME_CONTROL)
  1707. SwapScreenBuffer(); // Copy back buffer to front buffer (screen)
  1708. // Frame time control system
  1709. CORE.Time.current = GetTime();
  1710. CORE.Time.draw = CORE.Time.current - CORE.Time.previous;
  1711. CORE.Time.previous = CORE.Time.current;
  1712. CORE.Time.frame = CORE.Time.update + CORE.Time.draw;
  1713. // Wait for some milliseconds...
  1714. if (CORE.Time.frame < CORE.Time.target)
  1715. {
  1716. WaitTime((float)(CORE.Time.target - CORE.Time.frame)*1000.0f);
  1717. CORE.Time.current = GetTime();
  1718. double waitTime = CORE.Time.current - CORE.Time.previous;
  1719. CORE.Time.previous = CORE.Time.current;
  1720. CORE.Time.frame += waitTime; // Total frame time: update + draw + wait
  1721. }
  1722. PollInputEvents(); // Poll user events (before next frame update)
  1723. #endif
  1724. #if defined(SUPPORT_EVENTS_AUTOMATION)
  1725. // Events recording and playing logic
  1726. if (eventsRecording) RecordAutomationEvent(CORE.Time.frameCounter);
  1727. else if (eventsPlaying)
  1728. {
  1729. // TODO: When should we play? After/before/replace PollInputEvents()?
  1730. if (CORE.Time.frameCounter >= eventCount) eventsPlaying = false;
  1731. PlayAutomationEvent(CORE.Time.frameCounter);
  1732. }
  1733. #endif
  1734. CORE.Time.frameCounter++;
  1735. }
  1736. // Initialize 2D mode with custom camera (2D)
  1737. void BeginMode2D(Camera2D camera)
  1738. {
  1739. rlDrawRenderBatchActive(); // Update and draw internal render batch
  1740. rlLoadIdentity(); // Reset current matrix (modelview)
  1741. // Apply 2d camera transformation to modelview
  1742. rlMultMatrixf(MatrixToFloat(GetCameraMatrix2D(camera)));
  1743. // Apply screen scaling if required
  1744. rlMultMatrixf(MatrixToFloat(CORE.Window.screenScale));
  1745. }
  1746. // Ends 2D mode with custom camera
  1747. void EndMode2D(void)
  1748. {
  1749. rlDrawRenderBatchActive(); // Update and draw internal render batch
  1750. rlLoadIdentity(); // Reset current matrix (modelview)
  1751. rlMultMatrixf(MatrixToFloat(CORE.Window.screenScale)); // Apply screen scaling if required
  1752. }
  1753. // Initializes 3D mode with custom camera (3D)
  1754. void BeginMode3D(Camera3D camera)
  1755. {
  1756. rlDrawRenderBatchActive(); // Update and draw internal render batch
  1757. rlMatrixMode(RL_PROJECTION); // Switch to projection matrix
  1758. rlPushMatrix(); // Save previous matrix, which contains the settings for the 2d ortho projection
  1759. rlLoadIdentity(); // Reset current matrix (projection)
  1760. float aspect = (float)CORE.Window.currentFbo.width/(float)CORE.Window.currentFbo.height;
  1761. // NOTE: zNear and zFar values are important when computing depth buffer values
  1762. if (camera.projection == CAMERA_PERSPECTIVE)
  1763. {
  1764. // Setup perspective projection
  1765. double top = RL_CULL_DISTANCE_NEAR*tan(camera.fovy*0.5*DEG2RAD);
  1766. double right = top*aspect;
  1767. rlFrustum(-right, right, -top, top, RL_CULL_DISTANCE_NEAR, RL_CULL_DISTANCE_FAR);
  1768. }
  1769. else if (camera.projection == CAMERA_ORTHOGRAPHIC)
  1770. {
  1771. // Setup orthographic projection
  1772. double top = camera.fovy/2.0;
  1773. double right = top*aspect;
  1774. rlOrtho(-right, right, -top,top, RL_CULL_DISTANCE_NEAR, RL_CULL_DISTANCE_FAR);
  1775. }
  1776. rlMatrixMode(RL_MODELVIEW); // Switch back to modelview matrix
  1777. rlLoadIdentity(); // Reset current matrix (modelview)
  1778. // Setup Camera view
  1779. Matrix matView = MatrixLookAt(camera.position, camera.target, camera.up);
  1780. rlMultMatrixf(MatrixToFloat(matView)); // Multiply modelview matrix by view matrix (camera)
  1781. rlEnableDepthTest(); // Enable DEPTH_TEST for 3D
  1782. }
  1783. // Ends 3D mode and returns to default 2D orthographic mode
  1784. void EndMode3D(void)
  1785. {
  1786. rlDrawRenderBatchActive(); // Update and draw internal render batch
  1787. rlMatrixMode(RL_PROJECTION); // Switch to projection matrix
  1788. rlPopMatrix(); // Restore previous matrix (projection) from matrix stack
  1789. rlMatrixMode(RL_MODELVIEW); // Switch back to modelview matrix
  1790. rlLoadIdentity(); // Reset current matrix (modelview)
  1791. rlMultMatrixf(MatrixToFloat(CORE.Window.screenScale)); // Apply screen scaling if required
  1792. rlDisableDepthTest(); // Disable DEPTH_TEST for 2D
  1793. }
  1794. // Initializes render texture for drawing
  1795. void BeginTextureMode(RenderTexture2D target)
  1796. {
  1797. rlDrawRenderBatchActive(); // Update and draw internal render batch
  1798. rlEnableFramebuffer(target.id); // Enable render target
  1799. // Set viewport to framebuffer size
  1800. rlViewport(0, 0, target.texture.width, target.texture.height);
  1801. rlMatrixMode(RL_PROJECTION); // Switch to projection matrix
  1802. rlLoadIdentity(); // Reset current matrix (projection)
  1803. // Set orthographic projection to current framebuffer size
  1804. // NOTE: Configured top-left corner as (0, 0)
  1805. rlOrtho(0, target.texture.width, target.texture.height, 0, 0.0f, 1.0f);
  1806. rlMatrixMode(RL_MODELVIEW); // Switch back to modelview matrix
  1807. rlLoadIdentity(); // Reset current matrix (modelview)
  1808. //rlScalef(0.0f, -1.0f, 0.0f); // Flip Y-drawing (?)
  1809. // Setup current width/height for proper aspect ratio
  1810. // calculation when using BeginMode3D()
  1811. CORE.Window.currentFbo.width = target.texture.width;
  1812. CORE.Window.currentFbo.height = target.texture.height;
  1813. }
  1814. // Ends drawing to render texture
  1815. void EndTextureMode(void)
  1816. {
  1817. rlDrawRenderBatchActive(); // Update and draw internal render batch
  1818. rlDisableFramebuffer(); // Disable render target (fbo)
  1819. // Set viewport to default framebuffer size
  1820. SetupViewport(CORE.Window.render.width, CORE.Window.render.height);
  1821. // Reset current fbo to screen size
  1822. CORE.Window.currentFbo.width = CORE.Window.screen.width;
  1823. CORE.Window.currentFbo.height = CORE.Window.screen.height;
  1824. }
  1825. // Begin custom shader mode
  1826. void BeginShaderMode(Shader shader)
  1827. {
  1828. rlSetShader(shader.id, shader.locs);
  1829. }
  1830. // End custom shader mode (returns to default shader)
  1831. void EndShaderMode(void)
  1832. {
  1833. rlSetShader(rlGetShaderIdDefault(), rlGetShaderLocsDefault());
  1834. }
  1835. // Begin blending mode (alpha, additive, multiplied, subtract, custom)
  1836. // NOTE: Blend modes supported are enumerated in BlendMode enum
  1837. void BeginBlendMode(int mode)
  1838. {
  1839. rlSetBlendMode(mode);
  1840. }
  1841. // End blending mode (reset to default: alpha blending)
  1842. void EndBlendMode(void)
  1843. {
  1844. rlSetBlendMode(BLEND_ALPHA);
  1845. }
  1846. // Begin scissor mode (define screen area for following drawing)
  1847. // NOTE: Scissor rec refers to bottom-left corner, we change it to upper-left
  1848. void BeginScissorMode(int x, int y, int width, int height)
  1849. {
  1850. rlDrawRenderBatchActive(); // Update and draw internal render batch
  1851. rlEnableScissorTest();
  1852. rlScissor(x, CORE.Window.currentFbo.height - (y + height), width, height);
  1853. }
  1854. // End scissor mode
  1855. void EndScissorMode(void)
  1856. {
  1857. rlDrawRenderBatchActive(); // Update and draw internal render batch
  1858. rlDisableScissorTest();
  1859. }
  1860. // Begin VR drawing configuration
  1861. void BeginVrStereoMode(VrStereoConfig config)
  1862. {
  1863. rlEnableStereoRender();
  1864. // Set stereo render matrices
  1865. rlSetMatrixProjectionStereo(config.projection[0], config.projection[1]);
  1866. rlSetMatrixViewOffsetStereo(config.viewOffset[0], config.viewOffset[1]);
  1867. }
  1868. // End VR drawing process (and desktop mirror)
  1869. void EndVrStereoMode(void)
  1870. {
  1871. rlDisableStereoRender();
  1872. }
  1873. // Load VR stereo config for VR simulator device parameters
  1874. VrStereoConfig LoadVrStereoConfig(VrDeviceInfo device)
  1875. {
  1876. VrStereoConfig config = { 0 };
  1877. #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)
  1878. // Compute aspect ratio
  1879. float aspect = ((float)device.hResolution*0.5f)/(float)device.vResolution;
  1880. // Compute lens parameters
  1881. float lensShift = (device.hScreenSize*0.25f - device.lensSeparationDistance*0.5f)/device.hScreenSize;
  1882. config.leftLensCenter[0] = 0.25f + lensShift;
  1883. config.leftLensCenter[1] = 0.5f;
  1884. config.rightLensCenter[0] = 0.75f - lensShift;
  1885. config.rightLensCenter[1] = 0.5f;
  1886. config.leftScreenCenter[0] = 0.25f;
  1887. config.leftScreenCenter[1] = 0.5f;
  1888. config.rightScreenCenter[0] = 0.75f;
  1889. config.rightScreenCenter[1] = 0.5f;
  1890. // Compute distortion scale parameters
  1891. // NOTE: To get lens max radius, lensShift must be normalized to [-1..1]
  1892. float lensRadius = fabsf(-1.0f - 4.0f*lensShift);
  1893. float lensRadiusSq = lensRadius*lensRadius;
  1894. float distortionScale = device.lensDistortionValues[0] +
  1895. device.lensDistortionValues[1]*lensRadiusSq +
  1896. device.lensDistortionValues[2]*lensRadiusSq*lensRadiusSq +
  1897. device.lensDistortionValues[3]*lensRadiusSq*lensRadiusSq*lensRadiusSq;
  1898. float normScreenWidth = 0.5f;
  1899. float normScreenHeight = 1.0f;
  1900. config.scaleIn[0] = 2.0f/normScreenWidth;
  1901. config.scaleIn[1] = 2.0f/normScreenHeight/aspect;
  1902. config.scale[0] = normScreenWidth*0.5f/distortionScale;
  1903. config.scale[1] = normScreenHeight*0.5f*aspect/distortionScale;
  1904. // Fovy is normally computed with: 2*atan2f(device.vScreenSize, 2*device.eyeToScreenDistance)
  1905. // ...but with lens distortion it is increased (see Oculus SDK Documentation)
  1906. //float fovy = 2.0f*atan2f(device.vScreenSize*0.5f*distortionScale, device.eyeToScreenDistance); // Really need distortionScale?
  1907. float fovy = 2.0f*(float)atan2f(device.vScreenSize*0.5f, device.eyeToScreenDistance);
  1908. // Compute camera projection matrices
  1909. float projOffset = 4.0f*lensShift; // Scaled to projection space coordinates [-1..1]
  1910. Matrix proj = MatrixPerspective(fovy, aspect, RL_CULL_DISTANCE_NEAR, RL_CULL_DISTANCE_FAR);
  1911. config.projection[0] = MatrixMultiply(proj, MatrixTranslate(projOffset, 0.0f, 0.0f));
  1912. config.projection[1] = MatrixMultiply(proj, MatrixTranslate(-projOffset, 0.0f, 0.0f));
  1913. // Compute camera transformation matrices
  1914. // NOTE: Camera movement might seem more natural if we model the head.
  1915. // Our axis of rotation is the base of our head, so we might want to add
  1916. // some y (base of head to eye level) and -z (center of head to eye protrusion) to the camera positions.
  1917. config.viewOffset[0] = MatrixTranslate(-device.interpupillaryDistance*0.5f, 0.075f, 0.045f);
  1918. config.viewOffset[1] = MatrixTranslate(device.interpupillaryDistance*0.5f, 0.075f, 0.045f);
  1919. // Compute eyes Viewports
  1920. /*
  1921. config.eyeViewportRight[0] = 0;
  1922. config.eyeViewportRight[1] = 0;
  1923. config.eyeViewportRight[2] = device.hResolution/2;
  1924. config.eyeViewportRight[3] = device.vResolution;
  1925. config.eyeViewportLeft[0] = device.hResolution/2;
  1926. config.eyeViewportLeft[1] = 0;
  1927. config.eyeViewportLeft[2] = device.hResolution/2;
  1928. config.eyeViewportLeft[3] = device.vResolution;
  1929. */
  1930. #else
  1931. TRACELOG(LOG_WARNING, "RLGL: VR Simulator not supported on OpenGL 1.1");
  1932. #endif
  1933. return config;
  1934. }
  1935. // Unload VR stereo config properties
  1936. void UnloadVrStereoConfig(VrStereoConfig config)
  1937. {
  1938. //...
  1939. }
  1940. // Load shader from files and bind default locations
  1941. // NOTE: If shader string is NULL, using default vertex/fragment shaders
  1942. Shader LoadShader(const char *vsFileName, const char *fsFileName)
  1943. {
  1944. Shader shader = { 0 };
  1945. char *vShaderStr = NULL;
  1946. char *fShaderStr = NULL;
  1947. if (vsFileName != NULL) vShaderStr = LoadFileText(vsFileName);
  1948. if (fsFileName != NULL) fShaderStr = LoadFileText(fsFileName);
  1949. shader = LoadShaderFromMemory(vShaderStr, fShaderStr);
  1950. UnloadFileText(vShaderStr);
  1951. UnloadFileText(fShaderStr);
  1952. return shader;
  1953. }
  1954. // Load shader from code strings and bind default locations
  1955. RLAPI Shader LoadShaderFromMemory(const char *vsCode, const char *fsCode)
  1956. {
  1957. Shader shader = { 0 };
  1958. shader.locs = (int *)RL_CALLOC(RL_MAX_SHADER_LOCATIONS, sizeof(int));
  1959. // NOTE: All locations must be reseted to -1 (no location)
  1960. for (int i = 0; i < RL_MAX_SHADER_LOCATIONS; i++) shader.locs[i] = -1;
  1961. shader.id = rlLoadShaderCode(vsCode, fsCode);
  1962. // After shader loading, we TRY to set default location names
  1963. if (shader.id > 0)
  1964. {
  1965. // Default shader attribute locations have been binded before linking:
  1966. // vertex position location = 0
  1967. // vertex texcoord location = 1
  1968. // vertex normal location = 2
  1969. // vertex color location = 3
  1970. // vertex tangent location = 4
  1971. // vertex texcoord2 location = 5
  1972. // NOTE: If any location is not found, loc point becomes -1
  1973. // Get handles to GLSL input attibute locations
  1974. shader.locs[SHADER_LOC_VERTEX_POSITION] = rlGetLocationAttrib(shader.id, RL_DEFAULT_SHADER_ATTRIB_NAME_POSITION);
  1975. shader.locs[SHADER_LOC_VERTEX_TEXCOORD01] = rlGetLocationAttrib(shader.id, RL_DEFAULT_SHADER_ATTRIB_NAME_TEXCOORD);
  1976. shader.locs[SHADER_LOC_VERTEX_TEXCOORD02] = rlGetLocationAttrib(shader.id, RL_DEFAULT_SHADER_ATTRIB_NAME_TEXCOORD2);
  1977. shader.locs[SHADER_LOC_VERTEX_NORMAL] = rlGetLocationAttrib(shader.id, RL_DEFAULT_SHADER_ATTRIB_NAME_NORMAL);
  1978. shader.locs[SHADER_LOC_VERTEX_TANGENT] = rlGetLocationAttrib(shader.id, RL_DEFAULT_SHADER_ATTRIB_NAME_TANGENT);
  1979. shader.locs[SHADER_LOC_VERTEX_COLOR] = rlGetLocationAttrib(shader.id, RL_DEFAULT_SHADER_ATTRIB_NAME_COLOR);
  1980. // Get handles to GLSL uniform locations (vertex shader)
  1981. shader.locs[SHADER_LOC_MATRIX_MVP] = rlGetLocationUniform(shader.id, RL_DEFAULT_SHADER_UNIFORM_NAME_MVP);
  1982. shader.locs[SHADER_LOC_MATRIX_VIEW] = rlGetLocationUniform(shader.id, RL_DEFAULT_SHADER_UNIFORM_NAME_VIEW);
  1983. shader.locs[SHADER_LOC_MATRIX_PROJECTION] = rlGetLocationUniform(shader.id, RL_DEFAULT_SHADER_UNIFORM_NAME_PROJECTION);
  1984. shader.locs[SHADER_LOC_MATRIX_MODEL] = rlGetLocationUniform(shader.id, RL_DEFAULT_SHADER_UNIFORM_NAME_MODEL);
  1985. shader.locs[SHADER_LOC_MATRIX_NORMAL] = rlGetLocationUniform(shader.id, RL_DEFAULT_SHADER_UNIFORM_NAME_NORMAL);
  1986. // Get handles to GLSL uniform locations (fragment shader)
  1987. shader.locs[SHADER_LOC_COLOR_DIFFUSE] = rlGetLocationUniform(shader.id, RL_DEFAULT_SHADER_UNIFORM_NAME_COLOR);
  1988. shader.locs[SHADER_LOC_MAP_DIFFUSE] = rlGetLocationUniform(shader.id, RL_DEFAULT_SHADER_SAMPLER2D_NAME_TEXTURE0); // SHADER_LOC_MAP_ALBEDO
  1989. shader.locs[SHADER_LOC_MAP_SPECULAR] = rlGetLocationUniform(shader.id, RL_DEFAULT_SHADER_SAMPLER2D_NAME_TEXTURE1); // SHADER_LOC_MAP_METALNESS
  1990. shader.locs[SHADER_LOC_MAP_NORMAL] = rlGetLocationUniform(shader.id, RL_DEFAULT_SHADER_SAMPLER2D_NAME_TEXTURE2);
  1991. }
  1992. return shader;
  1993. }
  1994. // Unload shader from GPU memory (VRAM)
  1995. void UnloadShader(Shader shader)
  1996. {
  1997. if (shader.id != rlGetShaderIdDefault())
  1998. {
  1999. rlUnloadShaderProgram(shader.id);
  2000. RL_FREE(shader.locs);
  2001. }
  2002. }
  2003. // Get shader uniform location
  2004. int GetShaderLocation(Shader shader, const char *uniformName)
  2005. {
  2006. return rlGetLocationUniform(shader.id, uniformName);
  2007. }
  2008. // Get shader attribute location
  2009. int GetShaderLocationAttrib(Shader shader, const char *attribName)
  2010. {
  2011. return rlGetLocationAttrib(shader.id, attribName);
  2012. }
  2013. // Set shader uniform value
  2014. void SetShaderValue(Shader shader, int locIndex, const void *value, int uniformType)
  2015. {
  2016. SetShaderValueV(shader, locIndex, value, uniformType, 1);
  2017. }
  2018. // Set shader uniform value vector
  2019. void SetShaderValueV(Shader shader, int locIndex, const void *value, int uniformType, int count)
  2020. {
  2021. rlEnableShader(shader.id);
  2022. rlSetUniform(locIndex, value, uniformType, count);
  2023. //rlDisableShader(); // Avoid reseting current shader program, in case other uniforms are set
  2024. }
  2025. // Set shader uniform value (matrix 4x4)
  2026. void SetShaderValueMatrix(Shader shader, int locIndex, Matrix mat)
  2027. {
  2028. rlEnableShader(shader.id);
  2029. rlSetUniformMatrix(locIndex, mat);
  2030. //rlDisableShader();
  2031. }
  2032. // Set shader uniform value for texture
  2033. void SetShaderValueTexture(Shader shader, int locIndex, Texture2D texture)
  2034. {
  2035. rlEnableShader(shader.id);
  2036. rlSetUniformSampler(locIndex, texture.id);
  2037. //rlDisableShader();
  2038. }
  2039. // Get a ray trace from mouse position
  2040. Ray GetMouseRay(Vector2 mouse, Camera camera)
  2041. {
  2042. Ray ray = { 0 };
  2043. // Calculate normalized device coordinates
  2044. // NOTE: y value is negative
  2045. float x = (2.0f*mouse.x)/(float)GetScreenWidth() - 1.0f;
  2046. float y = 1.0f - (2.0f*mouse.y)/(float)GetScreenHeight();
  2047. float z = 1.0f;
  2048. // Store values in a vector
  2049. Vector3 deviceCoords = { x, y, z };
  2050. // Calculate view matrix from camera look at
  2051. Matrix matView = MatrixLookAt(camera.position, camera.target, camera.up);
  2052. Matrix matProj = MatrixIdentity();
  2053. if (camera.projection == CAMERA_PERSPECTIVE)
  2054. {
  2055. // Calculate projection matrix from perspective
  2056. matProj = MatrixPerspective(camera.fovy*DEG2RAD, ((double)GetScreenWidth()/(double)GetScreenHeight()), RL_CULL_DISTANCE_NEAR, RL_CULL_DISTANCE_FAR);
  2057. }
  2058. else if (camera.projection == CAMERA_ORTHOGRAPHIC)
  2059. {
  2060. float aspect = (float)CORE.Window.screen.width/(float)CORE.Window.screen.height;
  2061. double top = camera.fovy/2.0;
  2062. double right = top*aspect;
  2063. // Calculate projection matrix from orthographic
  2064. matProj = MatrixOrtho(-right, right, -top, top, 0.01, 1000.0);
  2065. }
  2066. // Unproject far/near points
  2067. Vector3 nearPoint = Vector3Unproject((Vector3){ deviceCoords.x, deviceCoords.y, 0.0f }, matProj, matView);
  2068. Vector3 farPoint = Vector3Unproject((Vector3){ deviceCoords.x, deviceCoords.y, 1.0f }, matProj, matView);
  2069. // Unproject the mouse cursor in the near plane.
  2070. // We need this as the source position because orthographic projects, compared to perspect doesn't have a
  2071. // convergence point, meaning that the "eye" of the camera is more like a plane than a point.
  2072. Vector3 cameraPlanePointerPos = Vector3Unproject((Vector3){ deviceCoords.x, deviceCoords.y, -1.0f }, matProj, matView);
  2073. // Calculate normalized direction vector
  2074. Vector3 direction = Vector3Normalize(Vector3Subtract(farPoint, nearPoint));
  2075. if (camera.projection == CAMERA_PERSPECTIVE) ray.position = camera.position;
  2076. else if (camera.projection == CAMERA_ORTHOGRAPHIC) ray.position = cameraPlanePointerPos;
  2077. // Apply calculated vectors to ray
  2078. ray.direction = direction;
  2079. return ray;
  2080. }
  2081. // Get transform matrix for camera
  2082. Matrix GetCameraMatrix(Camera camera)
  2083. {
  2084. return MatrixLookAt(camera.position, camera.target, camera.up);
  2085. }
  2086. // Get camera 2d transform matrix
  2087. Matrix GetCameraMatrix2D(Camera2D camera)
  2088. {
  2089. Matrix matTransform = { 0 };
  2090. // The camera in world-space is set by
  2091. // 1. Move it to target
  2092. // 2. Rotate by -rotation and scale by (1/zoom)
  2093. // When setting higher scale, it's more intuitive for the world to become bigger (= camera become smaller),
  2094. // not for the camera getting bigger, hence the invert. Same deal with rotation.
  2095. // 3. Move it by (-offset);
  2096. // Offset defines target transform relative to screen, but since we're effectively "moving" screen (camera)
  2097. // we need to do it into opposite direction (inverse transform)
  2098. // Having camera transform in world-space, inverse of it gives the modelview transform.
  2099. // Since (A*B*C)' = C'*B'*A', the modelview is
  2100. // 1. Move to offset
  2101. // 2. Rotate and Scale
  2102. // 3. Move by -target
  2103. Matrix matOrigin = MatrixTranslate(-camera.target.x, -camera.target.y, 0.0f);
  2104. Matrix matRotation = MatrixRotate((Vector3){ 0.0f, 0.0f, 1.0f }, camera.rotation*DEG2RAD);
  2105. Matrix matScale = MatrixScale(camera.zoom, camera.zoom, 1.0f);
  2106. Matrix matTranslation = MatrixTranslate(camera.offset.x, camera.offset.y, 0.0f);
  2107. matTransform = MatrixMultiply(MatrixMultiply(matOrigin, MatrixMultiply(matScale, matRotation)), matTranslation);
  2108. return matTransform;
  2109. }
  2110. // Get the screen space position from a 3d world space position
  2111. Vector2 GetWorldToScreen(Vector3 position, Camera camera)
  2112. {
  2113. Vector2 screenPosition = GetWorldToScreenEx(position, camera, GetScreenWidth(), GetScreenHeight());
  2114. return screenPosition;
  2115. }
  2116. // Get size position for a 3d world space position (useful for texture drawing)
  2117. Vector2 GetWorldToScreenEx(Vector3 position, Camera camera, int width, int height)
  2118. {
  2119. // Calculate projection matrix (from perspective instead of frustum
  2120. Matrix matProj = MatrixIdentity();
  2121. if (camera.projection == CAMERA_PERSPECTIVE)
  2122. {
  2123. // Calculate projection matrix from perspective
  2124. matProj = MatrixPerspective(camera.fovy*DEG2RAD, ((double)width/(double)height), RL_CULL_DISTANCE_NEAR, RL_CULL_DISTANCE_FAR);
  2125. }
  2126. else if (camera.projection == CAMERA_ORTHOGRAPHIC)
  2127. {
  2128. float aspect = (float)CORE.Window.screen.width/(float)CORE.Window.screen.height;
  2129. double top = camera.fovy/2.0;
  2130. double right = top*aspect;
  2131. // Calculate projection matrix from orthographic
  2132. matProj = MatrixOrtho(-right, right, -top, top, RL_CULL_DISTANCE_NEAR, RL_CULL_DISTANCE_FAR);
  2133. }
  2134. // Calculate view matrix from camera look at (and transpose it)
  2135. Matrix matView = MatrixLookAt(camera.position, camera.target, camera.up);
  2136. // Convert world position vector to quaternion
  2137. Quaternion worldPos = { position.x, position.y, position.z, 1.0f };
  2138. // Transform world position to view
  2139. worldPos = QuaternionTransform(worldPos, matView);
  2140. // Transform result to projection (clip space position)
  2141. worldPos = QuaternionTransform(worldPos, matProj);
  2142. // Calculate normalized device coordinates (inverted y)
  2143. Vector3 ndcPos = { worldPos.x/worldPos.w, -worldPos.y/worldPos.w, worldPos.z/worldPos.w };
  2144. // Calculate 2d screen position vector
  2145. Vector2 screenPosition = { (ndcPos.x + 1.0f)/2.0f*(float)width, (ndcPos.y + 1.0f)/2.0f*(float)height };
  2146. return screenPosition;
  2147. }
  2148. // Get the screen space position for a 2d camera world space position
  2149. Vector2 GetWorldToScreen2D(Vector2 position, Camera2D camera)
  2150. {
  2151. Matrix matCamera = GetCameraMatrix2D(camera);
  2152. Vector3 transform = Vector3Transform((Vector3){ position.x, position.y, 0 }, matCamera);
  2153. return (Vector2){ transform.x, transform.y };
  2154. }
  2155. // Get the world space position for a 2d camera screen space position
  2156. Vector2 GetScreenToWorld2D(Vector2 position, Camera2D camera)
  2157. {
  2158. Matrix invMatCamera = MatrixInvert(GetCameraMatrix2D(camera));
  2159. Vector3 transform = Vector3Transform((Vector3){ position.x, position.y, 0 }, invMatCamera);
  2160. return (Vector2){ transform.x, transform.y };
  2161. }
  2162. // Set target FPS (maximum)
  2163. void SetTargetFPS(int fps)
  2164. {
  2165. if (fps < 1) CORE.Time.target = 0.0;
  2166. else CORE.Time.target = 1.0/(double)fps;
  2167. TRACELOG(LOG_INFO, "TIMER: Target time per frame: %02.03f milliseconds", (float)CORE.Time.target*1000);
  2168. }
  2169. // Get current FPS
  2170. // NOTE: We calculate an average framerate
  2171. int GetFPS(void)
  2172. {
  2173. int fps = 0;
  2174. #if !defined(SUPPORT_CUSTOM_FRAME_CONTROL)
  2175. #define FPS_CAPTURE_FRAMES_COUNT 30 // 30 captures
  2176. #define FPS_AVERAGE_TIME_SECONDS 0.5f // 500 millisecondes
  2177. #define FPS_STEP (FPS_AVERAGE_TIME_SECONDS/FPS_CAPTURE_FRAMES_COUNT)
  2178. static int index = 0;
  2179. static float history[FPS_CAPTURE_FRAMES_COUNT] = { 0 };
  2180. static float average = 0, last = 0;
  2181. float fpsFrame = GetFrameTime();
  2182. if (fpsFrame == 0) return 0;
  2183. if ((GetTime() - last) > FPS_STEP)
  2184. {
  2185. last = (float)GetTime();
  2186. index = (index + 1)%FPS_CAPTURE_FRAMES_COUNT;
  2187. average -= history[index];
  2188. history[index] = fpsFrame/FPS_CAPTURE_FRAMES_COUNT;
  2189. average += history[index];
  2190. }
  2191. fps = (int)roundf(1.0f/average);
  2192. #endif
  2193. return fps;
  2194. }
  2195. // Get time in seconds for last frame drawn (delta time)
  2196. float GetFrameTime(void)
  2197. {
  2198. return (float)CORE.Time.frame;
  2199. }
  2200. // Get elapsed time measure in seconds since InitTimer()
  2201. // NOTE: On PLATFORM_DESKTOP InitTimer() is called on InitWindow()
  2202. // NOTE: On PLATFORM_DESKTOP, timer is initialized on glfwInit()
  2203. double GetTime(void)
  2204. {
  2205. #if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB)
  2206. return glfwGetTime(); // Elapsed time since glfwInit()
  2207. #endif
  2208. #if defined(PLATFORM_ANDROID) || defined(PLATFORM_RPI) || defined(PLATFORM_DRM)
  2209. struct timespec ts = { 0 };
  2210. clock_gettime(CLOCK_MONOTONIC, &ts);
  2211. unsigned long long int time = (unsigned long long int)ts.tv_sec*1000000000LLU + (unsigned long long int)ts.tv_nsec;
  2212. return (double)(time - CORE.Time.base)*1e-9; // Elapsed time since InitTimer()
  2213. #endif
  2214. }
  2215. // Setup window configuration flags (view FLAGS)
  2216. // NOTE: This function is expected to be called before window creation,
  2217. // because it setups some flags for the window creation process.
  2218. // To configure window states after creation, just use SetWindowState()
  2219. void SetConfigFlags(unsigned int flags)
  2220. {
  2221. // Selected flags are set but not evaluated at this point,
  2222. // flag evaluation happens at InitWindow() or SetWindowState()
  2223. CORE.Window.flags |= flags;
  2224. }
  2225. // NOTE TRACELOG() function is located in [utils.h]
  2226. // Takes a screenshot of current screen (saved a .png)
  2227. void TakeScreenshot(const char *fileName)
  2228. {
  2229. unsigned char *imgData = rlReadScreenPixels(CORE.Window.render.width, CORE.Window.render.height);
  2230. Image image = { imgData, CORE.Window.render.width, CORE.Window.render.height, 1, PIXELFORMAT_UNCOMPRESSED_R8G8B8A8 };
  2231. char path[512] = { 0 };
  2232. strcpy(path, TextFormat("%s/%s", CORE.Storage.basePath, fileName));
  2233. ExportImage(image, path);
  2234. RL_FREE(imgData);
  2235. #if defined(PLATFORM_WEB)
  2236. // Download file from MEMFS (emscripten memory filesystem)
  2237. // saveFileFromMEMFSToDisk() function is defined in raylib/src/shell.html
  2238. emscripten_run_script(TextFormat("saveFileFromMEMFSToDisk('%s','%s')", GetFileName(path), GetFileName(path)));
  2239. #endif
  2240. // TODO: Verification required for log
  2241. TRACELOG(LOG_INFO, "SYSTEM: [%s] Screenshot taken successfully", path);
  2242. }
  2243. // Get a random value between min and max (both included)
  2244. int GetRandomValue(int min, int max)
  2245. {
  2246. if (min > max)
  2247. {
  2248. int tmp = max;
  2249. max = min;
  2250. min = tmp;
  2251. }
  2252. return (rand()%(abs(max - min) + 1) + min);
  2253. }
  2254. // Set the seed for the random number generator
  2255. void SetRandomSeed(unsigned int seed)
  2256. {
  2257. srand(seed);
  2258. }
  2259. // Check if the file exists
  2260. bool FileExists(const char *fileName)
  2261. {
  2262. bool result = false;
  2263. #if defined(_WIN32)
  2264. if (_access(fileName, 0) != -1) result = true;
  2265. #else
  2266. if (access(fileName, F_OK) != -1) result = true;
  2267. #endif
  2268. return result;
  2269. }
  2270. // Check file extension
  2271. // NOTE: Extensions checking is not case-sensitive
  2272. bool IsFileExtension(const char *fileName, const char *ext)
  2273. {
  2274. bool result = false;
  2275. const char *fileExt = GetFileExtension(fileName);
  2276. if (fileExt != NULL)
  2277. {
  2278. #if defined(SUPPORT_TEXT_MANIPULATION)
  2279. int extCount = 0;
  2280. const char **checkExts = TextSplit(ext, ';', &extCount);
  2281. char fileExtLower[16] = { 0 };
  2282. strcpy(fileExtLower, TextToLower(fileExt));
  2283. for (int i = 0; i < extCount; i++)
  2284. {
  2285. if (TextIsEqual(fileExtLower, TextToLower(checkExts[i])))
  2286. {
  2287. result = true;
  2288. break;
  2289. }
  2290. }
  2291. #else
  2292. if (strcmp(fileExt, ext) == 0) result = true;
  2293. #endif
  2294. }
  2295. return result;
  2296. }
  2297. // Check if a directory path exists
  2298. bool DirectoryExists(const char *dirPath)
  2299. {
  2300. bool result = false;
  2301. DIR *dir = opendir(dirPath);
  2302. if (dir != NULL)
  2303. {
  2304. result = true;
  2305. closedir(dir);
  2306. }
  2307. return result;
  2308. }
  2309. // Get pointer to extension for a filename string (includes the dot: .png)
  2310. const char *GetFileExtension(const char *fileName)
  2311. {
  2312. const char *dot = strrchr(fileName, '.');
  2313. if (!dot || dot == fileName) return NULL;
  2314. return dot;
  2315. }
  2316. // String pointer reverse break: returns right-most occurrence of charset in s
  2317. static const char *strprbrk(const char *s, const char *charset)
  2318. {
  2319. const char *latestMatch = NULL;
  2320. for (; s = strpbrk(s, charset), s != NULL; latestMatch = s++) { }
  2321. return latestMatch;
  2322. }
  2323. // Get pointer to filename for a path string
  2324. const char *GetFileName(const char *filePath)
  2325. {
  2326. const char *fileName = NULL;
  2327. if (filePath != NULL) fileName = strprbrk(filePath, "\\/");
  2328. if (!fileName) return filePath;
  2329. return fileName + 1;
  2330. }
  2331. // Get filename string without extension (uses static string)
  2332. const char *GetFileNameWithoutExt(const char *filePath)
  2333. {
  2334. #define MAX_FILENAMEWITHOUTEXT_LENGTH 128
  2335. static char fileName[MAX_FILENAMEWITHOUTEXT_LENGTH] = { 0 };
  2336. memset(fileName, 0, MAX_FILENAMEWITHOUTEXT_LENGTH);
  2337. if (filePath != NULL) strcpy(fileName, GetFileName(filePath)); // Get filename with extension
  2338. int size = (int)strlen(fileName); // Get size in bytes
  2339. for (int i = 0; (i < size) && (i < MAX_FILENAMEWITHOUTEXT_LENGTH); i++)
  2340. {
  2341. if (fileName[i] == '.')
  2342. {
  2343. // NOTE: We break on first '.' found
  2344. fileName[i] = '\0';
  2345. break;
  2346. }
  2347. }
  2348. return fileName;
  2349. }
  2350. // Get directory for a given filePath
  2351. const char *GetDirectoryPath(const char *filePath)
  2352. {
  2353. /*
  2354. // NOTE: Directory separator is different in Windows and other platforms,
  2355. // fortunately, Windows also support the '/' separator, that's the one should be used
  2356. #if defined(_WIN32)
  2357. char separator = '\\';
  2358. #else
  2359. char separator = '/';
  2360. #endif
  2361. */
  2362. const char *lastSlash = NULL;
  2363. static char dirPath[MAX_FILEPATH_LENGTH] = { 0 };
  2364. memset(dirPath, 0, MAX_FILEPATH_LENGTH);
  2365. // In case provided path does not contain a root drive letter (C:\, D:\) nor leading path separator (\, /),
  2366. // we add the current directory path to dirPath
  2367. if (filePath[1] != ':' && filePath[0] != '\\' && filePath[0] != '/')
  2368. {
  2369. // For security, we set starting path to current directory,
  2370. // obtained path will be concated to this
  2371. dirPath[0] = '.';
  2372. dirPath[1] = '/';
  2373. }
  2374. lastSlash = strprbrk(filePath, "\\/");
  2375. if (lastSlash)
  2376. {
  2377. if (lastSlash == filePath)
  2378. {
  2379. // The last and only slash is the leading one: path is in a root directory
  2380. dirPath[0] = filePath[0];
  2381. dirPath[1] = '\0';
  2382. }
  2383. else
  2384. {
  2385. // NOTE: Be careful, strncpy() is not safe, it does not care about '\0'
  2386. memcpy(dirPath + (filePath[1] != ':' && filePath[0] != '\\' && filePath[0] != '/' ? 2 : 0), filePath, strlen(filePath) - (strlen(lastSlash) - 1));
  2387. dirPath[strlen(filePath) - strlen(lastSlash) + (filePath[1] != ':' && filePath[0] != '\\' && filePath[0] != '/' ? 2 : 0)] = '\0'; // Add '\0' manually
  2388. }
  2389. }
  2390. return dirPath;
  2391. }
  2392. // Get previous directory path for a given path
  2393. const char *GetPrevDirectoryPath(const char *dirPath)
  2394. {
  2395. static char prevDirPath[MAX_FILEPATH_LENGTH] = { 0 };
  2396. memset(prevDirPath, 0, MAX_FILEPATH_LENGTH);
  2397. int pathLen = (int)strlen(dirPath);
  2398. if (pathLen <= 3) strcpy(prevDirPath, dirPath);
  2399. for (int i = (pathLen - 1); (i >= 0) && (pathLen > 3); i--)
  2400. {
  2401. if ((dirPath[i] == '\\') || (dirPath[i] == '/'))
  2402. {
  2403. // Check for root: "C:\" or "/"
  2404. if (((i == 2) && (dirPath[1] ==':')) || (i == 0)) i++;
  2405. strncpy(prevDirPath, dirPath, i);
  2406. break;
  2407. }
  2408. }
  2409. return prevDirPath;
  2410. }
  2411. // Get current working directory
  2412. const char *GetWorkingDirectory(void)
  2413. {
  2414. static char currentDir[MAX_FILEPATH_LENGTH] = { 0 };
  2415. memset(currentDir, 0, MAX_FILEPATH_LENGTH);
  2416. char *path = GETCWD(currentDir, MAX_FILEPATH_LENGTH - 1);
  2417. return path;
  2418. }
  2419. // Get filenames in a directory path (max 512 files)
  2420. // NOTE: Files count is returned by parameters pointer
  2421. char **GetDirectoryFiles(const char *dirPath, int *fileCount)
  2422. {
  2423. #define MAX_DIRECTORY_FILES 512
  2424. ClearDirectoryFiles();
  2425. // Memory allocation for MAX_DIRECTORY_FILES
  2426. dirFilesPath = (char **)RL_MALLOC(MAX_DIRECTORY_FILES*sizeof(char *));
  2427. for (int i = 0; i < MAX_DIRECTORY_FILES; i++) dirFilesPath[i] = (char *)RL_MALLOC(MAX_FILEPATH_LENGTH*sizeof(char));
  2428. int counter = 0;
  2429. struct dirent *entity;
  2430. DIR *dir = opendir(dirPath);
  2431. if (dir != NULL) // It's a directory
  2432. {
  2433. // TODO: Reading could be done in two passes,
  2434. // first one to count files and second one to read names
  2435. // That way we can allocate required memory, instead of a limited pool
  2436. while ((entity = readdir(dir)) != NULL)
  2437. {
  2438. strcpy(dirFilesPath[counter], entity->d_name);
  2439. counter++;
  2440. }
  2441. closedir(dir);
  2442. }
  2443. else TRACELOG(LOG_WARNING, "FILEIO: Failed to open requested directory"); // Maybe it's a file...
  2444. dirFileCount = counter;
  2445. *fileCount = dirFileCount;
  2446. return dirFilesPath;
  2447. }
  2448. // Clear directory files paths buffers
  2449. void ClearDirectoryFiles(void)
  2450. {
  2451. if (dirFileCount > 0)
  2452. {
  2453. for (int i = 0; i < MAX_DIRECTORY_FILES; i++) RL_FREE(dirFilesPath[i]);
  2454. RL_FREE(dirFilesPath);
  2455. }
  2456. dirFileCount = 0;
  2457. }
  2458. // Change working directory, returns true on success
  2459. bool ChangeDirectory(const char *dir)
  2460. {
  2461. bool result = CHDIR(dir);
  2462. if (result != 0) TRACELOG(LOG_WARNING, "SYSTEM: Failed to change to directory: %s", dir);
  2463. return (result == 0);
  2464. }
  2465. // Check if a file has been dropped into window
  2466. bool IsFileDropped(void)
  2467. {
  2468. if (CORE.Window.dropFileCount > 0) return true;
  2469. else return false;
  2470. }
  2471. // Get dropped files names
  2472. char **GetDroppedFiles(int *count)
  2473. {
  2474. *count = CORE.Window.dropFileCount;
  2475. return CORE.Window.dropFilesPath;
  2476. }
  2477. // Clear dropped files paths buffer
  2478. void ClearDroppedFiles(void)
  2479. {
  2480. if (CORE.Window.dropFileCount > 0)
  2481. {
  2482. for (int i = 0; i < CORE.Window.dropFileCount; i++) RL_FREE(CORE.Window.dropFilesPath[i]);
  2483. RL_FREE(CORE.Window.dropFilesPath);
  2484. CORE.Window.dropFileCount = 0;
  2485. }
  2486. }
  2487. // Get file modification time (last write time)
  2488. long GetFileModTime(const char *fileName)
  2489. {
  2490. struct stat result = { 0 };
  2491. if (stat(fileName, &result) == 0)
  2492. {
  2493. time_t mod = result.st_mtime;
  2494. return (long)mod;
  2495. }
  2496. return 0;
  2497. }
  2498. // Compress data (DEFLATE algorythm)
  2499. unsigned char *CompressData(unsigned char *data, int dataLength, int *compDataLength)
  2500. {
  2501. #define COMPRESSION_QUALITY_DEFLATE 8
  2502. unsigned char *compData = NULL;
  2503. #if defined(SUPPORT_COMPRESSION_API)
  2504. // Compress data and generate a valid DEFLATE stream
  2505. struct sdefl sdefl = { 0 };
  2506. int bounds = sdefl_bound(dataLength);
  2507. compData = (unsigned char *)RL_CALLOC(bounds, 1);
  2508. *compDataLength = sdeflate(&sdefl, compData, data, dataLength, COMPRESSION_QUALITY_DEFLATE); // Compression level 8, same as stbwi
  2509. TraceLog(LOG_INFO, "SYSTEM: Compress data: Original size: %i -> Comp. size: %i", dataLength, *compDataLength);
  2510. #endif
  2511. return compData;
  2512. }
  2513. // Decompress data (DEFLATE algorythm)
  2514. unsigned char *DecompressData(unsigned char *compData, int compDataLength, int *dataLength)
  2515. {
  2516. unsigned char *data = NULL;
  2517. #if defined(SUPPORT_COMPRESSION_API)
  2518. // Decompress data from a valid DEFLATE stream
  2519. data = RL_CALLOC(MAX_DECOMPRESSION_SIZE*1024*1024, 1);
  2520. int length = sinflate(data, compData, compDataLength);
  2521. unsigned char *temp = RL_REALLOC(data, length);
  2522. if (temp != NULL) data = temp;
  2523. else TRACELOG(LOG_WARNING, "SYSTEM: Failed to re-allocate required decompression memory");
  2524. *dataLength = length;
  2525. TraceLog(LOG_INFO, "SYSTEM: Decompress data: Comp. size: %i -> Original size: %i", compDataLength, *dataLength);
  2526. #endif
  2527. return data;
  2528. }
  2529. // Save integer value to storage file (to defined position)
  2530. // NOTE: Storage positions is directly related to file memory layout (4 bytes each integer)
  2531. bool SaveStorageValue(unsigned int position, int value)
  2532. {
  2533. bool success = false;
  2534. #if defined(SUPPORT_DATA_STORAGE)
  2535. char path[512] = { 0 };
  2536. strcpy(path, TextFormat("%s/%s", CORE.Storage.basePath, STORAGE_DATA_FILE));
  2537. unsigned int dataSize = 0;
  2538. unsigned int newDataSize = 0;
  2539. unsigned char *fileData = LoadFileData(path, &dataSize);
  2540. unsigned char *newFileData = NULL;
  2541. if (fileData != NULL)
  2542. {
  2543. if (dataSize <= (position*sizeof(int)))
  2544. {
  2545. // Increase data size up to position and store value
  2546. newDataSize = (position + 1)*sizeof(int);
  2547. newFileData = (unsigned char *)RL_REALLOC(fileData, newDataSize);
  2548. if (newFileData != NULL)
  2549. {
  2550. // RL_REALLOC succeded
  2551. int *dataPtr = (int *)newFileData;
  2552. dataPtr[position] = value;
  2553. }
  2554. else
  2555. {
  2556. // RL_REALLOC failed
  2557. TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to realloc data (%u), position in bytes (%u) bigger than actual file size", path, dataSize, position*sizeof(int));
  2558. // We store the old size of the file
  2559. newFileData = fileData;
  2560. newDataSize = dataSize;
  2561. }
  2562. }
  2563. else
  2564. {
  2565. // Store the old size of the file
  2566. newFileData = fileData;
  2567. newDataSize = dataSize;
  2568. // Replace value on selected position
  2569. int *dataPtr = (int *)newFileData;
  2570. dataPtr[position] = value;
  2571. }
  2572. success = SaveFileData(path, newFileData, newDataSize);
  2573. RL_FREE(newFileData);
  2574. TRACELOG(LOG_INFO, "FILEIO: [%s] Saved storage value: %i", path, value);
  2575. }
  2576. else
  2577. {
  2578. TRACELOG(LOG_INFO, "FILEIO: [%s] File created successfully", path);
  2579. dataSize = (position + 1)*sizeof(int);
  2580. fileData = (unsigned char *)RL_MALLOC(dataSize);
  2581. int *dataPtr = (int *)fileData;
  2582. dataPtr[position] = value;
  2583. success = SaveFileData(path, fileData, dataSize);
  2584. UnloadFileData(fileData);
  2585. TRACELOG(LOG_INFO, "FILEIO: [%s] Saved storage value: %i", path, value);
  2586. }
  2587. #endif
  2588. return success;
  2589. }
  2590. // Load integer value from storage file (from defined position)
  2591. // NOTE: If requested position could not be found, value 0 is returned
  2592. int LoadStorageValue(unsigned int position)
  2593. {
  2594. int value = 0;
  2595. #if defined(SUPPORT_DATA_STORAGE)
  2596. char path[512] = { 0 };
  2597. strcpy(path, TextFormat("%s/%s", CORE.Storage.basePath, STORAGE_DATA_FILE));
  2598. unsigned int dataSize = 0;
  2599. unsigned char *fileData = LoadFileData(path, &dataSize);
  2600. if (fileData != NULL)
  2601. {
  2602. if (dataSize < (position*4)) TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to find storage position: %i", path, position);
  2603. else
  2604. {
  2605. int *dataPtr = (int *)fileData;
  2606. value = dataPtr[position];
  2607. }
  2608. UnloadFileData(fileData);
  2609. TRACELOG(LOG_INFO, "FILEIO: [%s] Loaded storage value: %i", path, value);
  2610. }
  2611. #endif
  2612. return value;
  2613. }
  2614. // Open URL with default system browser (if available)
  2615. // NOTE: This function is only safe to use if you control the URL given.
  2616. // A user could craft a malicious string performing another action.
  2617. // Only call this function yourself not with user input or make sure to check the string yourself.
  2618. // Ref: https://github.com/raysan5/raylib/issues/686
  2619. void OpenURL(const char *url)
  2620. {
  2621. // Small security check trying to avoid (partially) malicious code...
  2622. // sorry for the inconvenience when you hit this point...
  2623. if (strchr(url, '\'') != NULL)
  2624. {
  2625. TRACELOG(LOG_WARNING, "SYSTEM: Provided URL is not valid");
  2626. }
  2627. else
  2628. {
  2629. #if defined(PLATFORM_DESKTOP)
  2630. char *cmd = (char *)RL_CALLOC(strlen(url) + 10, sizeof(char));
  2631. #if defined(_WIN32)
  2632. sprintf(cmd, "explorer %s", url);
  2633. #endif
  2634. #if defined(__linux__) || defined(__FreeBSD__)
  2635. sprintf(cmd, "xdg-open '%s'", url); // Alternatives: firefox, x-www-browser
  2636. #endif
  2637. #if defined(__APPLE__)
  2638. sprintf(cmd, "open '%s'", url);
  2639. #endif
  2640. system(cmd);
  2641. RL_FREE(cmd);
  2642. #endif
  2643. #if defined(PLATFORM_WEB)
  2644. emscripten_run_script(TextFormat("window.open('%s', '_blank')", url));
  2645. #endif
  2646. }
  2647. }
  2648. //----------------------------------------------------------------------------------
  2649. // Module Functions Definition - Input (Keyboard, Mouse, Gamepad) Functions
  2650. //----------------------------------------------------------------------------------
  2651. // Check if a key has been pressed once
  2652. bool IsKeyPressed(int key)
  2653. {
  2654. bool pressed = false;
  2655. if ((CORE.Input.Keyboard.previousKeyState[key] == 0) && (CORE.Input.Keyboard.currentKeyState[key] == 1)) pressed = true;
  2656. else pressed = false;
  2657. return pressed;
  2658. }
  2659. // Check if a key is being pressed (key held down)
  2660. bool IsKeyDown(int key)
  2661. {
  2662. if (CORE.Input.Keyboard.currentKeyState[key] == 1) return true;
  2663. else return false;
  2664. }
  2665. // Check if a key has been released once
  2666. bool IsKeyReleased(int key)
  2667. {
  2668. bool released = false;
  2669. if ((CORE.Input.Keyboard.previousKeyState[key] == 1) && (CORE.Input.Keyboard.currentKeyState[key] == 0)) released = true;
  2670. else released = false;
  2671. return released;
  2672. }
  2673. // Check if a key is NOT being pressed (key not held down)
  2674. bool IsKeyUp(int key)
  2675. {
  2676. if (CORE.Input.Keyboard.currentKeyState[key] == 0) return true;
  2677. else return false;
  2678. }
  2679. // Get the last key pressed
  2680. int GetKeyPressed(void)
  2681. {
  2682. int value = 0;
  2683. if (CORE.Input.Keyboard.keyPressedQueueCount > 0)
  2684. {
  2685. // Get character from the queue head
  2686. value = CORE.Input.Keyboard.keyPressedQueue[0];
  2687. // Shift elements 1 step toward the head.
  2688. for (int i = 0; i < (CORE.Input.Keyboard.keyPressedQueueCount - 1); i++)
  2689. CORE.Input.Keyboard.keyPressedQueue[i] = CORE.Input.Keyboard.keyPressedQueue[i + 1];
  2690. // Reset last character in the queue
  2691. CORE.Input.Keyboard.keyPressedQueue[CORE.Input.Keyboard.keyPressedQueueCount] = 0;
  2692. CORE.Input.Keyboard.keyPressedQueueCount--;
  2693. }
  2694. return value;
  2695. }
  2696. // Get the last char pressed
  2697. int GetCharPressed(void)
  2698. {
  2699. int value = 0;
  2700. if (CORE.Input.Keyboard.charPressedQueueCount > 0)
  2701. {
  2702. // Get character from the queue head
  2703. value = CORE.Input.Keyboard.charPressedQueue[0];
  2704. // Shift elements 1 step toward the head.
  2705. for (int i = 0; i < (CORE.Input.Keyboard.charPressedQueueCount - 1); i++)
  2706. CORE.Input.Keyboard.charPressedQueue[i] = CORE.Input.Keyboard.charPressedQueue[i + 1];
  2707. // Reset last character in the queue
  2708. CORE.Input.Keyboard.charPressedQueue[CORE.Input.Keyboard.charPressedQueueCount] = 0;
  2709. CORE.Input.Keyboard.charPressedQueueCount--;
  2710. }
  2711. return value;
  2712. }
  2713. // Set a custom key to exit program
  2714. // NOTE: default exitKey is ESCAPE
  2715. void SetExitKey(int key)
  2716. {
  2717. #if !defined(PLATFORM_ANDROID)
  2718. CORE.Input.Keyboard.exitKey = key;
  2719. #endif
  2720. }
  2721. // NOTE: Gamepad support not implemented in emscripten GLFW3 (PLATFORM_WEB)
  2722. // Check if a gamepad is available
  2723. bool IsGamepadAvailable(int gamepad)
  2724. {
  2725. bool result = false;
  2726. if ((gamepad < MAX_GAMEPADS) && CORE.Input.Gamepad.ready[gamepad]) result = true;
  2727. return result;
  2728. }
  2729. // Check gamepad name (if available)
  2730. bool IsGamepadName(int gamepad, const char *name)
  2731. {
  2732. bool result = false;
  2733. const char *currentName = NULL;
  2734. if (CORE.Input.Gamepad.ready[gamepad]) currentName = GetGamepadName(gamepad);
  2735. if ((name != NULL) && (currentName != NULL)) result = (strcmp(name, currentName) == 0);
  2736. return result;
  2737. }
  2738. // Get gamepad internal name id
  2739. const char *GetGamepadName(int gamepad)
  2740. {
  2741. #if defined(PLATFORM_DESKTOP)
  2742. if (CORE.Input.Gamepad.ready[gamepad]) return glfwGetJoystickName(gamepad);
  2743. else return NULL;
  2744. #endif
  2745. #if defined(PLATFORM_RPI) || defined(PLATFORM_DRM)
  2746. if (CORE.Input.Gamepad.ready[gamepad]) ioctl(CORE.Input.Gamepad.streamId[gamepad], JSIOCGNAME(64), &CORE.Input.Gamepad.name[gamepad]);
  2747. return CORE.Input.Gamepad.name[gamepad];
  2748. #endif
  2749. #if defined(PLATFORM_WEB)
  2750. return CORE.Input.Gamepad.name[gamepad];
  2751. #endif
  2752. return NULL;
  2753. }
  2754. // Get gamepad axis count
  2755. int GetGamepadAxisCount(int gamepad)
  2756. {
  2757. #if defined(PLATFORM_RPI) || defined(PLATFORM_DRM)
  2758. int axisCount = 0;
  2759. if (CORE.Input.Gamepad.ready[gamepad]) ioctl(CORE.Input.Gamepad.streamId[gamepad], JSIOCGAXES, &axisCount);
  2760. CORE.Input.Gamepad.axisCount = axisCount;
  2761. #endif
  2762. return CORE.Input.Gamepad.axisCount;
  2763. }
  2764. // Get axis movement vector for a gamepad
  2765. float GetGamepadAxisMovement(int gamepad, int axis)
  2766. {
  2767. float value = 0;
  2768. if ((gamepad < MAX_GAMEPADS) && CORE.Input.Gamepad.ready[gamepad] && (axis < MAX_GAMEPAD_AXIS) &&
  2769. (fabsf(CORE.Input.Gamepad.axisState[gamepad][axis]) > 0.1f)) value = CORE.Input.Gamepad.axisState[gamepad][axis]; // 0.1f = GAMEPAD_AXIS_MINIMUM_DRIFT/DELTA
  2770. return value;
  2771. }
  2772. // Check if a gamepad button has been pressed once
  2773. bool IsGamepadButtonPressed(int gamepad, int button)
  2774. {
  2775. bool pressed = false;
  2776. if ((gamepad < MAX_GAMEPADS) && CORE.Input.Gamepad.ready[gamepad] && (button < MAX_GAMEPAD_BUTTONS) &&
  2777. (CORE.Input.Gamepad.previousButtonState[gamepad][button] == 0) && (CORE.Input.Gamepad.currentButtonState[gamepad][button] == 1)) pressed = true;
  2778. else pressed = false;
  2779. return pressed;
  2780. }
  2781. // Check if a gamepad button is being pressed
  2782. bool IsGamepadButtonDown(int gamepad, int button)
  2783. {
  2784. bool result = false;
  2785. if ((gamepad < MAX_GAMEPADS) && CORE.Input.Gamepad.ready[gamepad] && (button < MAX_GAMEPAD_BUTTONS) &&
  2786. (CORE.Input.Gamepad.currentButtonState[gamepad][button] == 1)) result = true;
  2787. return result;
  2788. }
  2789. // Check if a gamepad button has NOT been pressed once
  2790. bool IsGamepadButtonReleased(int gamepad, int button)
  2791. {
  2792. bool released = false;
  2793. if ((gamepad < MAX_GAMEPADS) && CORE.Input.Gamepad.ready[gamepad] && (button < MAX_GAMEPAD_BUTTONS) &&
  2794. (CORE.Input.Gamepad.previousButtonState[gamepad][button] == 1) && (CORE.Input.Gamepad.currentButtonState[gamepad][button] == 0)) released = true;
  2795. else released = false;
  2796. return released;
  2797. }
  2798. // Check if a gamepad button is NOT being pressed
  2799. bool IsGamepadButtonUp(int gamepad, int button)
  2800. {
  2801. bool result = false;
  2802. if ((gamepad < MAX_GAMEPADS) && CORE.Input.Gamepad.ready[gamepad] && (button < MAX_GAMEPAD_BUTTONS) &&
  2803. (CORE.Input.Gamepad.currentButtonState[gamepad][button] == 0)) result = true;
  2804. return result;
  2805. }
  2806. // Get the last gamepad button pressed
  2807. int GetGamepadButtonPressed(void)
  2808. {
  2809. return CORE.Input.Gamepad.lastButtonPressed;
  2810. }
  2811. // Set internal gamepad mappings
  2812. int SetGamepadMappings(const char *mappings)
  2813. {
  2814. int result = 0;
  2815. #if defined(PLATFORM_DESKTOP)
  2816. result = glfwUpdateGamepadMappings(mappings);
  2817. #endif
  2818. return result;
  2819. }
  2820. // Check if a mouse button has been pressed once
  2821. bool IsMouseButtonPressed(int button)
  2822. {
  2823. bool pressed = false;
  2824. if ((CORE.Input.Mouse.currentButtonState[button] == 1) && (CORE.Input.Mouse.previousButtonState[button] == 0)) pressed = true;
  2825. // Map touches to mouse buttons checking
  2826. if ((CORE.Input.Touch.currentTouchState[button] == 1) && (CORE.Input.Touch.previousTouchState[button] == 0)) pressed = true;
  2827. return pressed;
  2828. }
  2829. // Check if a mouse button is being pressed
  2830. bool IsMouseButtonDown(int button)
  2831. {
  2832. bool down = false;
  2833. if (CORE.Input.Mouse.currentButtonState[button] == 1) down = true;
  2834. // Map touches to mouse buttons checking
  2835. if (CORE.Input.Touch.currentTouchState[button] == 1) down = true;
  2836. return down;
  2837. }
  2838. // Check if a mouse button has been released once
  2839. bool IsMouseButtonReleased(int button)
  2840. {
  2841. bool released = false;
  2842. if ((CORE.Input.Mouse.currentButtonState[button] == 0) && (CORE.Input.Mouse.previousButtonState[button] == 1)) released = true;
  2843. // Map touches to mouse buttons checking
  2844. if ((CORE.Input.Touch.currentTouchState[button] == 0) && (CORE.Input.Touch.previousTouchState[button] == 1)) released = true;
  2845. return released;
  2846. }
  2847. // Check if a mouse button is NOT being pressed
  2848. bool IsMouseButtonUp(int button)
  2849. {
  2850. return !IsMouseButtonDown(button);
  2851. }
  2852. // Get mouse position X
  2853. int GetMouseX(void)
  2854. {
  2855. #if defined(PLATFORM_ANDROID)
  2856. return (int)CORE.Input.Touch.position[0].x;
  2857. #else
  2858. return (int)((CORE.Input.Mouse.currentPosition.x + CORE.Input.Mouse.offset.x)*CORE.Input.Mouse.scale.x);
  2859. #endif
  2860. }
  2861. // Get mouse position Y
  2862. int GetMouseY(void)
  2863. {
  2864. #if defined(PLATFORM_ANDROID)
  2865. return (int)CORE.Input.Touch.position[0].y;
  2866. #else
  2867. return (int)((CORE.Input.Mouse.currentPosition.y + CORE.Input.Mouse.offset.y)*CORE.Input.Mouse.scale.y);
  2868. #endif
  2869. }
  2870. // Get mouse position XY
  2871. Vector2 GetMousePosition(void)
  2872. {
  2873. Vector2 position = { 0 };
  2874. #if defined(PLATFORM_ANDROID) || defined(PLATFORM_WEB)
  2875. position = GetTouchPosition(0);
  2876. #else
  2877. position.x = (CORE.Input.Mouse.currentPosition.x + CORE.Input.Mouse.offset.x)*CORE.Input.Mouse.scale.x;
  2878. position.y = (CORE.Input.Mouse.currentPosition.y + CORE.Input.Mouse.offset.y)*CORE.Input.Mouse.scale.y;
  2879. #endif
  2880. return position;
  2881. }
  2882. // Get mouse delta between frames
  2883. Vector2 GetMouseDelta(void)
  2884. {
  2885. Vector2 delta = {0};
  2886. delta.x = CORE.Input.Mouse.currentPosition.x - CORE.Input.Mouse.previousPosition.x;
  2887. delta.y = CORE.Input.Mouse.currentPosition.y - CORE.Input.Mouse.previousPosition.y;
  2888. return delta;
  2889. }
  2890. // Set mouse position XY
  2891. void SetMousePosition(int x, int y)
  2892. {
  2893. CORE.Input.Mouse.currentPosition = (Vector2){ (float)x, (float)y };
  2894. #if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB)
  2895. // NOTE: emscripten not implemented
  2896. glfwSetCursorPos(CORE.Window.handle, CORE.Input.Mouse.currentPosition.x, CORE.Input.Mouse.currentPosition.y);
  2897. #endif
  2898. }
  2899. // Set mouse offset
  2900. // NOTE: Useful when rendering to different size targets
  2901. void SetMouseOffset(int offsetX, int offsetY)
  2902. {
  2903. CORE.Input.Mouse.offset = (Vector2){ (float)offsetX, (float)offsetY };
  2904. }
  2905. // Set mouse scaling
  2906. // NOTE: Useful when rendering to different size targets
  2907. void SetMouseScale(float scaleX, float scaleY)
  2908. {
  2909. CORE.Input.Mouse.scale = (Vector2){ scaleX, scaleY };
  2910. }
  2911. // Get mouse wheel movement Y
  2912. float GetMouseWheelMove(void)
  2913. {
  2914. #if defined(PLATFORM_ANDROID)
  2915. return 0.0f;
  2916. #endif
  2917. #if defined(PLATFORM_WEB)
  2918. return CORE.Input.Mouse.previousWheelMove/100.0f;
  2919. #endif
  2920. return CORE.Input.Mouse.previousWheelMove;
  2921. }
  2922. // Set mouse cursor
  2923. // NOTE: This is a no-op on platforms other than PLATFORM_DESKTOP
  2924. void SetMouseCursor(int cursor)
  2925. {
  2926. #if defined(PLATFORM_DESKTOP)
  2927. CORE.Input.Mouse.cursor = cursor;
  2928. if (cursor == MOUSE_CURSOR_DEFAULT) glfwSetCursor(CORE.Window.handle, NULL);
  2929. else
  2930. {
  2931. // NOTE: We are relating internal GLFW enum values to our MouseCursor enum values
  2932. glfwSetCursor(CORE.Window.handle, glfwCreateStandardCursor(0x00036000 + cursor));
  2933. }
  2934. #endif
  2935. }
  2936. // Get touch position X for touch point 0 (relative to screen size)
  2937. int GetTouchX(void)
  2938. {
  2939. #if defined(PLATFORM_ANDROID) || defined(PLATFORM_WEB)
  2940. return (int)CORE.Input.Touch.position[0].x;
  2941. #else // PLATFORM_DESKTOP, PLATFORM_RPI, PLATFORM_DRM
  2942. return GetMouseX();
  2943. #endif
  2944. }
  2945. // Get touch position Y for touch point 0 (relative to screen size)
  2946. int GetTouchY(void)
  2947. {
  2948. #if defined(PLATFORM_ANDROID) || defined(PLATFORM_WEB)
  2949. return (int)CORE.Input.Touch.position[0].y;
  2950. #else // PLATFORM_DESKTOP, PLATFORM_RPI, PLATFORM_DRM
  2951. return GetMouseY();
  2952. #endif
  2953. }
  2954. // Get touch position XY for a touch point index (relative to screen size)
  2955. // TODO: Touch position should be scaled depending on display size and render size
  2956. Vector2 GetTouchPosition(int index)
  2957. {
  2958. Vector2 position = { -1.0f, -1.0f };
  2959. #if defined(PLATFORM_DESKTOP)
  2960. // TODO: GLFW does not support multi-touch input just yet
  2961. // https://www.codeproject.com/Articles/668404/Programming-for-Multi-Touch
  2962. // https://docs.microsoft.com/en-us/windows/win32/wintouch/getting-started-with-multi-touch-messages
  2963. if (index == 0) position = GetMousePosition();
  2964. #endif
  2965. #if defined(PLATFORM_ANDROID)
  2966. if (index < MAX_TOUCH_POINTS) position = CORE.Input.Touch.position[index];
  2967. else TRACELOG(LOG_WARNING, "INPUT: Required touch point out of range (Max touch points: %i)", MAX_TOUCH_POINTS);
  2968. if ((CORE.Window.screen.width > CORE.Window.display.width) || (CORE.Window.screen.height > CORE.Window.display.height))
  2969. {
  2970. position.x = position.x*((float)CORE.Window.screen.width/(float)(CORE.Window.display.width - CORE.Window.renderOffset.x)) - CORE.Window.renderOffset.x/2;
  2971. position.y = position.y*((float)CORE.Window.screen.height/(float)(CORE.Window.display.height - CORE.Window.renderOffset.y)) - CORE.Window.renderOffset.y/2;
  2972. }
  2973. else
  2974. {
  2975. position.x = position.x*((float)CORE.Window.render.width/(float)CORE.Window.display.width) - CORE.Window.renderOffset.x/2;
  2976. position.y = position.y*((float)CORE.Window.render.height/(float)CORE.Window.display.height) - CORE.Window.renderOffset.y/2;
  2977. }
  2978. #endif
  2979. #if defined(PLATFORM_WEB) || defined(PLATFORM_RPI) || defined(PLATFORM_DRM)
  2980. if (index < MAX_TOUCH_POINTS) position = CORE.Input.Touch.position[index];
  2981. else TRACELOG(LOG_WARNING, "INPUT: Required touch point out of range (Max touch points: %i)", MAX_TOUCH_POINTS);
  2982. // TODO: Touch position scaling required?
  2983. #endif
  2984. return position;
  2985. }
  2986. // Get number of touch points
  2987. int GetTouchPointCount(void)
  2988. {
  2989. return CORE.Input.Touch.pointCount;
  2990. }
  2991. //----------------------------------------------------------------------------------
  2992. // Module specific Functions Definition
  2993. //----------------------------------------------------------------------------------
  2994. // Initialize display device and framebuffer
  2995. // NOTE: width and height represent the screen (framebuffer) desired size, not actual display size
  2996. // If width or height are 0, default display size will be used for framebuffer size
  2997. // NOTE: returns false in case graphic device could not be created
  2998. static bool InitGraphicsDevice(int width, int height)
  2999. {
  3000. CORE.Window.screen.width = width; // User desired width
  3001. CORE.Window.screen.height = height; // User desired height
  3002. CORE.Window.screenScale = MatrixIdentity(); // No draw scaling required by default
  3003. // NOTE: Framebuffer (render area - CORE.Window.render.width, CORE.Window.render.height) could include black bars...
  3004. // ...in top-down or left-right to match display aspect ratio (no weird scalings)
  3005. #if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB)
  3006. glfwSetErrorCallback(ErrorCallback);
  3007. /*
  3008. // Setup custom allocators to match raylib ones
  3009. const GLFWallocator allocator = {
  3010. .allocate = MemAlloc,
  3011. .deallocate = MemFree,
  3012. .reallocate = MemRealloc,
  3013. .user = NULL
  3014. };
  3015. glfwInitAllocator(&allocator);
  3016. */
  3017. #if defined(__APPLE__)
  3018. glfwInitHint(GLFW_COCOA_CHDIR_RESOURCES, GLFW_FALSE);
  3019. #endif
  3020. if (!glfwInit())
  3021. {
  3022. TRACELOG(LOG_WARNING, "GLFW: Failed to initialize GLFW");
  3023. return false;
  3024. }
  3025. // NOTE: Getting video modes is not implemented in emscripten GLFW3 version
  3026. #if defined(PLATFORM_DESKTOP)
  3027. // Find monitor resolution
  3028. GLFWmonitor *monitor = glfwGetPrimaryMonitor();
  3029. if (!monitor)
  3030. {
  3031. TRACELOG(LOG_WARNING, "GLFW: Failed to get primary monitor");
  3032. return false;
  3033. }
  3034. const GLFWvidmode *mode = glfwGetVideoMode(monitor);
  3035. CORE.Window.display.width = mode->width;
  3036. CORE.Window.display.height = mode->height;
  3037. // Set screen width/height to the display width/height if they are 0
  3038. if (CORE.Window.screen.width == 0) CORE.Window.screen.width = CORE.Window.display.width;
  3039. if (CORE.Window.screen.height == 0) CORE.Window.screen.height = CORE.Window.display.height;
  3040. #endif // PLATFORM_DESKTOP
  3041. #if defined(PLATFORM_WEB)
  3042. CORE.Window.display.width = CORE.Window.screen.width;
  3043. CORE.Window.display.height = CORE.Window.screen.height;
  3044. #endif // PLATFORM_WEB
  3045. glfwDefaultWindowHints(); // Set default windows hints
  3046. //glfwWindowHint(GLFW_RED_BITS, 8); // Framebuffer red color component bits
  3047. //glfwWindowHint(GLFW_GREEN_BITS, 8); // Framebuffer green color component bits
  3048. //glfwWindowHint(GLFW_BLUE_BITS, 8); // Framebuffer blue color component bits
  3049. //glfwWindowHint(GLFW_ALPHA_BITS, 8); // Framebuffer alpha color component bits
  3050. //glfwWindowHint(GLFW_DEPTH_BITS, 24); // Depthbuffer bits
  3051. //glfwWindowHint(GLFW_REFRESH_RATE, 0); // Refresh rate for fullscreen window
  3052. //glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_API); // OpenGL API to use. Alternative: GLFW_OPENGL_ES_API
  3053. //glfwWindowHint(GLFW_AUX_BUFFERS, 0); // Number of auxiliar buffers
  3054. // Check window creation flags
  3055. if ((CORE.Window.flags & FLAG_FULLSCREEN_MODE) > 0) CORE.Window.fullscreen = true;
  3056. if ((CORE.Window.flags & FLAG_WINDOW_HIDDEN) > 0) glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE); // Visible window
  3057. else glfwWindowHint(GLFW_VISIBLE, GLFW_TRUE); // Window initially hidden
  3058. if ((CORE.Window.flags & FLAG_WINDOW_UNDECORATED) > 0) glfwWindowHint(GLFW_DECORATED, GLFW_FALSE); // Border and buttons on Window
  3059. else glfwWindowHint(GLFW_DECORATED, GLFW_TRUE); // Decorated window
  3060. if ((CORE.Window.flags & FLAG_WINDOW_RESIZABLE) > 0) glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE); // Resizable window
  3061. else glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE); // Avoid window being resizable
  3062. // Disable FLAG_WINDOW_MINIMIZED, not supported on initialization
  3063. if ((CORE.Window.flags & FLAG_WINDOW_MINIMIZED) > 0) CORE.Window.flags &= ~FLAG_WINDOW_MINIMIZED;
  3064. // Disable FLAG_WINDOW_MAXIMIZED, not supported on initialization
  3065. if ((CORE.Window.flags & FLAG_WINDOW_MAXIMIZED) > 0) CORE.Window.flags &= ~FLAG_WINDOW_MAXIMIZED;
  3066. if ((CORE.Window.flags & FLAG_WINDOW_UNFOCUSED) > 0) glfwWindowHint(GLFW_FOCUSED, GLFW_FALSE);
  3067. else glfwWindowHint(GLFW_FOCUSED, GLFW_TRUE);
  3068. if ((CORE.Window.flags & FLAG_WINDOW_TOPMOST) > 0) glfwWindowHint(GLFW_FLOATING, GLFW_TRUE);
  3069. else glfwWindowHint(GLFW_FLOATING, GLFW_FALSE);
  3070. // NOTE: Some GLFW flags are not supported on HTML5
  3071. #if defined(PLATFORM_DESKTOP)
  3072. if ((CORE.Window.flags & FLAG_WINDOW_TRANSPARENT) > 0) glfwWindowHint(GLFW_TRANSPARENT_FRAMEBUFFER, GLFW_TRUE); // Transparent framebuffer
  3073. else glfwWindowHint(GLFW_TRANSPARENT_FRAMEBUFFER, GLFW_FALSE); // Opaque framebuffer
  3074. if ((CORE.Window.flags & FLAG_WINDOW_HIGHDPI) > 0)
  3075. {
  3076. // Resize window content area based on the monitor content scale.
  3077. // NOTE: This hint only has an effect on platforms where screen coordinates and pixels always map 1:1 such as Windows and X11.
  3078. // On platforms like macOS the resolution of the framebuffer is changed independently of the window size.
  3079. glfwWindowHint(GLFW_SCALE_TO_MONITOR, GLFW_TRUE); // Scale content area based on the monitor content scale where window is placed on
  3080. #if defined(__APPLE__)
  3081. glfwWindowHint(GLFW_COCOA_RETINA_FRAMEBUFFER, GLFW_TRUE);
  3082. #endif
  3083. }
  3084. else glfwWindowHint(GLFW_SCALE_TO_MONITOR, GLFW_FALSE);
  3085. #endif
  3086. if (CORE.Window.flags & FLAG_MSAA_4X_HINT)
  3087. {
  3088. // NOTE: MSAA is only enabled for main framebuffer, not user-created FBOs
  3089. TRACELOG(LOG_INFO, "DISPLAY: Trying to enable MSAA x4");
  3090. glfwWindowHint(GLFW_SAMPLES, 4); // Tries to enable multisampling x4 (MSAA), default is 0
  3091. }
  3092. // NOTE: When asking for an OpenGL context version, most drivers provide highest supported version
  3093. // with forward compatibility to older OpenGL versions.
  3094. // For example, if using OpenGL 1.1, driver can provide a 4.3 context forward compatible.
  3095. // Check selection OpenGL version
  3096. if (rlGetVersion() == OPENGL_21)
  3097. {
  3098. glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 2); // Choose OpenGL major version (just hint)
  3099. glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 1); // Choose OpenGL minor version (just hint)
  3100. }
  3101. else if (rlGetVersion() == OPENGL_33)
  3102. {
  3103. glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); // Choose OpenGL major version (just hint)
  3104. glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); // Choose OpenGL minor version (just hint)
  3105. glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); // Profiles Hint: Only 3.3 and above!
  3106. // Values: GLFW_OPENGL_CORE_PROFILE, GLFW_OPENGL_ANY_PROFILE, GLFW_OPENGL_COMPAT_PROFILE
  3107. #if defined(__APPLE__)
  3108. glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GLFW_TRUE); // OSX Requires fordward compatibility
  3109. #else
  3110. glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GLFW_FALSE); // Fordward Compatibility Hint: Only 3.3 and above!
  3111. #endif
  3112. //glfwWindowHint(GLFW_OPENGL_DEBUG_CONTEXT, GLFW_TRUE); // Request OpenGL DEBUG context
  3113. }
  3114. else if (rlGetVersion() == OPENGL_ES_20) // Request OpenGL ES 2.0 context
  3115. {
  3116. glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 2);
  3117. glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0);
  3118. glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_ES_API);
  3119. #if defined(PLATFORM_DESKTOP)
  3120. glfwWindowHint(GLFW_CONTEXT_CREATION_API, GLFW_EGL_CONTEXT_API);
  3121. #else
  3122. glfwWindowHint(GLFW_CONTEXT_CREATION_API, GLFW_NATIVE_CONTEXT_API);
  3123. #endif
  3124. }
  3125. #if defined(PLATFORM_DESKTOP)
  3126. // NOTE: GLFW 3.4+ defers initialization of the Joystick subsystem on the first call to any Joystick related functions.
  3127. // Forcing this initialization here avoids doing it on `PollInputEvents` called by `EndDrawing` after first frame has been just drawn.
  3128. // The initialization will still happen and possible delays still occur, but before the window is shown, which is a nicer experience.
  3129. // REF: https://github.com/raysan5/raylib/issues/1554
  3130. if (MAX_GAMEPADS > 0) glfwSetJoystickCallback(NULL);
  3131. #endif
  3132. if (CORE.Window.fullscreen)
  3133. {
  3134. // remember center for switchinging from fullscreen to window
  3135. CORE.Window.position.x = CORE.Window.display.width/2 - CORE.Window.screen.width/2;
  3136. CORE.Window.position.y = CORE.Window.display.height/2 - CORE.Window.screen.height/2;
  3137. if (CORE.Window.position.x < 0) CORE.Window.position.x = 0;
  3138. if (CORE.Window.position.y < 0) CORE.Window.position.y = 0;
  3139. // Obtain recommended CORE.Window.display.width/CORE.Window.display.height from a valid videomode for the monitor
  3140. int count = 0;
  3141. const GLFWvidmode *modes = glfwGetVideoModes(glfwGetPrimaryMonitor(), &count);
  3142. // Get closest video mode to desired CORE.Window.screen.width/CORE.Window.screen.height
  3143. for (int i = 0; i < count; i++)
  3144. {
  3145. if ((unsigned int)modes[i].width >= CORE.Window.screen.width)
  3146. {
  3147. if ((unsigned int)modes[i].height >= CORE.Window.screen.height)
  3148. {
  3149. CORE.Window.display.width = modes[i].width;
  3150. CORE.Window.display.height = modes[i].height;
  3151. break;
  3152. }
  3153. }
  3154. }
  3155. #if defined(PLATFORM_DESKTOP)
  3156. // If we are windowed fullscreen, ensures that window does not minimize when focus is lost
  3157. if ((CORE.Window.screen.height == CORE.Window.display.height) && (CORE.Window.screen.width == CORE.Window.display.width))
  3158. {
  3159. glfwWindowHint(GLFW_AUTO_ICONIFY, 0);
  3160. }
  3161. #endif
  3162. TRACELOG(LOG_WARNING, "SYSTEM: Closest fullscreen videomode: %i x %i", CORE.Window.display.width, CORE.Window.display.height);
  3163. // NOTE: ISSUE: Closest videomode could not match monitor aspect-ratio, for example,
  3164. // for a desired screen size of 800x450 (16:9), closest supported videomode is 800x600 (4:3),
  3165. // framebuffer is rendered correctly but once displayed on a 16:9 monitor, it gets stretched
  3166. // by the sides to fit all monitor space...
  3167. // Try to setup the most appropiate fullscreen framebuffer for the requested screenWidth/screenHeight
  3168. // It considers device display resolution mode and setups a framebuffer with black bars if required (render size/offset)
  3169. // Modified global variables: CORE.Window.screen.width/CORE.Window.screen.height - CORE.Window.render.width/CORE.Window.render.height - CORE.Window.renderOffset.x/CORE.Window.renderOffset.y - CORE.Window.screenScale
  3170. // TODO: It is a quite cumbersome solution to display size vs requested size, it should be reviewed or removed...
  3171. // HighDPI monitors are properly considered in a following similar function: SetupViewport()
  3172. SetupFramebuffer(CORE.Window.display.width, CORE.Window.display.height);
  3173. CORE.Window.handle = glfwCreateWindow(CORE.Window.display.width, CORE.Window.display.height, (CORE.Window.title != 0)? CORE.Window.title : " ", glfwGetPrimaryMonitor(), NULL);
  3174. // NOTE: Full-screen change, not working properly...
  3175. //glfwSetWindowMonitor(CORE.Window.handle, glfwGetPrimaryMonitor(), 0, 0, CORE.Window.screen.width, CORE.Window.screen.height, GLFW_DONT_CARE);
  3176. }
  3177. else
  3178. {
  3179. // No-fullscreen window creation
  3180. CORE.Window.handle = glfwCreateWindow(CORE.Window.screen.width, CORE.Window.screen.height, (CORE.Window.title != 0)? CORE.Window.title : " ", NULL, NULL);
  3181. if (CORE.Window.handle)
  3182. {
  3183. #if defined(PLATFORM_DESKTOP)
  3184. // Center window on screen
  3185. int windowPosX = CORE.Window.display.width/2 - CORE.Window.screen.width/2;
  3186. int windowPosY = CORE.Window.display.height/2 - CORE.Window.screen.height/2;
  3187. if (windowPosX < 0) windowPosX = 0;
  3188. if (windowPosY < 0) windowPosY = 0;
  3189. glfwSetWindowPos(CORE.Window.handle, windowPosX, windowPosY);
  3190. #endif
  3191. CORE.Window.render.width = CORE.Window.screen.width;
  3192. CORE.Window.render.height = CORE.Window.screen.height;
  3193. }
  3194. }
  3195. if (!CORE.Window.handle)
  3196. {
  3197. glfwTerminate();
  3198. TRACELOG(LOG_WARNING, "GLFW: Failed to initialize Window");
  3199. return false;
  3200. }
  3201. else
  3202. {
  3203. TRACELOG(LOG_INFO, "DISPLAY: Device initialized successfully");
  3204. #if defined(PLATFORM_DESKTOP)
  3205. TRACELOG(LOG_INFO, " > Display size: %i x %i", CORE.Window.display.width, CORE.Window.display.height);
  3206. #endif
  3207. TRACELOG(LOG_INFO, " > Render size: %i x %i", CORE.Window.render.width, CORE.Window.render.height);
  3208. TRACELOG(LOG_INFO, " > Screen size: %i x %i", CORE.Window.screen.width, CORE.Window.screen.height);
  3209. TRACELOG(LOG_INFO, " > Viewport offsets: %i, %i", CORE.Window.renderOffset.x, CORE.Window.renderOffset.y);
  3210. }
  3211. // Set window callback events
  3212. glfwSetWindowSizeCallback(CORE.Window.handle, WindowSizeCallback); // NOTE: Resizing not allowed by default!
  3213. #if !defined(PLATFORM_WEB)
  3214. glfwSetWindowMaximizeCallback(CORE.Window.handle, WindowMaximizeCallback);
  3215. #endif
  3216. glfwSetWindowIconifyCallback(CORE.Window.handle, WindowIconifyCallback);
  3217. glfwSetWindowFocusCallback(CORE.Window.handle, WindowFocusCallback);
  3218. glfwSetDropCallback(CORE.Window.handle, WindowDropCallback);
  3219. // Set input callback events
  3220. glfwSetKeyCallback(CORE.Window.handle, KeyCallback);
  3221. glfwSetCharCallback(CORE.Window.handle, CharCallback);
  3222. glfwSetMouseButtonCallback(CORE.Window.handle, MouseButtonCallback);
  3223. glfwSetCursorPosCallback(CORE.Window.handle, MouseCursorPosCallback); // Track mouse position changes
  3224. glfwSetScrollCallback(CORE.Window.handle, MouseScrollCallback);
  3225. glfwSetCursorEnterCallback(CORE.Window.handle, CursorEnterCallback);
  3226. glfwMakeContextCurrent(CORE.Window.handle);
  3227. #if !defined(PLATFORM_WEB)
  3228. glfwSwapInterval(0); // No V-Sync by default
  3229. #endif
  3230. // Try to enable GPU V-Sync, so frames are limited to screen refresh rate (60Hz -> 60 FPS)
  3231. // NOTE: V-Sync can be enabled by graphic driver configuration
  3232. if (CORE.Window.flags & FLAG_VSYNC_HINT)
  3233. {
  3234. // WARNING: It seems to hits a critical render path in Intel HD Graphics
  3235. glfwSwapInterval(1);
  3236. TRACELOG(LOG_INFO, "DISPLAY: Trying to enable VSYNC");
  3237. }
  3238. #endif // PLATFORM_DESKTOP || PLATFORM_WEB
  3239. #if defined(PLATFORM_ANDROID) || defined(PLATFORM_RPI) || defined(PLATFORM_DRM)
  3240. CORE.Window.fullscreen = true;
  3241. CORE.Window.flags |= FLAG_FULLSCREEN_MODE;
  3242. #if defined(PLATFORM_RPI)
  3243. bcm_host_init();
  3244. DISPMANX_ELEMENT_HANDLE_T dispmanElement = { 0 };
  3245. DISPMANX_DISPLAY_HANDLE_T dispmanDisplay = { 0 };
  3246. DISPMANX_UPDATE_HANDLE_T dispmanUpdate = { 0 };
  3247. VC_RECT_T dstRect = { 0 };
  3248. VC_RECT_T srcRect = { 0 };
  3249. #endif
  3250. #if defined(PLATFORM_DRM)
  3251. CORE.Window.fd = -1;
  3252. CORE.Window.connector = NULL;
  3253. CORE.Window.modeIndex = -1;
  3254. CORE.Window.crtc = NULL;
  3255. CORE.Window.gbmDevice = NULL;
  3256. CORE.Window.gbmSurface = NULL;
  3257. CORE.Window.prevBO = NULL;
  3258. CORE.Window.prevFB = 0;
  3259. #if defined(DEFAULT_GRAPHIC_DEVICE_DRM)
  3260. CORE.Window.fd = open(DEFAULT_GRAPHIC_DEVICE_DRM, O_RDWR);
  3261. #else
  3262. TRACELOG(LOG_INFO, "DISPLAY: No graphic card set, trying card1");
  3263. CORE.Window.fd = open("/dev/dri/card1", O_RDWR); // VideoCore VI (Raspberry Pi 4)
  3264. if ((-1 == CORE.Window.fd) || (drmModeGetResources(CORE.Window.fd) == NULL))
  3265. {
  3266. TRACELOG(LOG_INFO, "DISPLAY: Failed to open graphic card1, trying card0");
  3267. CORE.Window.fd = open("/dev/dri/card0", O_RDWR); // VideoCore IV (Raspberry Pi 1-3)
  3268. }
  3269. #endif
  3270. if (-1 == CORE.Window.fd)
  3271. {
  3272. TRACELOG(LOG_WARNING, "DISPLAY: Failed to open graphic card");
  3273. return false;
  3274. }
  3275. drmModeRes *res = drmModeGetResources(CORE.Window.fd);
  3276. if (!res)
  3277. {
  3278. TRACELOG(LOG_WARNING, "DISPLAY: Failed get DRM resources");
  3279. return false;
  3280. }
  3281. TRACELOG(LOG_TRACE, "DISPLAY: Connectors found: %i", res->count_connectors);
  3282. for (size_t i = 0; i < res->count_connectors; i++)
  3283. {
  3284. TRACELOG(LOG_TRACE, "DISPLAY: Connector index %i", i);
  3285. drmModeConnector *con = drmModeGetConnector(CORE.Window.fd, res->connectors[i]);
  3286. TRACELOG(LOG_TRACE, "DISPLAY: Connector modes detected: %i", con->count_modes);
  3287. if ((con->connection == DRM_MODE_CONNECTED) && (con->encoder_id))
  3288. {
  3289. TRACELOG(LOG_TRACE, "DISPLAY: DRM mode connected");
  3290. CORE.Window.connector = con;
  3291. break;
  3292. }
  3293. else
  3294. {
  3295. TRACELOG(LOG_TRACE, "DISPLAY: DRM mode NOT connected (deleting)");
  3296. drmModeFreeConnector(con);
  3297. }
  3298. }
  3299. if (!CORE.Window.connector)
  3300. {
  3301. TRACELOG(LOG_WARNING, "DISPLAY: No suitable DRM connector found");
  3302. drmModeFreeResources(res);
  3303. return false;
  3304. }
  3305. drmModeEncoder *enc = drmModeGetEncoder(CORE.Window.fd, CORE.Window.connector->encoder_id);
  3306. if (!enc)
  3307. {
  3308. TRACELOG(LOG_WARNING, "DISPLAY: Failed to get DRM mode encoder");
  3309. drmModeFreeResources(res);
  3310. return false;
  3311. }
  3312. CORE.Window.crtc = drmModeGetCrtc(CORE.Window.fd, enc->crtc_id);
  3313. if (!CORE.Window.crtc)
  3314. {
  3315. TRACELOG(LOG_WARNING, "DISPLAY: Failed to get DRM mode crtc");
  3316. drmModeFreeEncoder(enc);
  3317. drmModeFreeResources(res);
  3318. return false;
  3319. }
  3320. // If InitWindow should use the current mode find it in the connector's mode list
  3321. if ((CORE.Window.screen.width <= 0) || (CORE.Window.screen.height <= 0))
  3322. {
  3323. TRACELOG(LOG_TRACE, "DISPLAY: Selecting DRM connector mode for current used mode...");
  3324. CORE.Window.modeIndex = FindMatchingConnectorMode(CORE.Window.connector, &CORE.Window.crtc->mode);
  3325. if (CORE.Window.modeIndex < 0)
  3326. {
  3327. TRACELOG(LOG_WARNING, "DISPLAY: No matching DRM connector mode found");
  3328. drmModeFreeEncoder(enc);
  3329. drmModeFreeResources(res);
  3330. return false;
  3331. }
  3332. CORE.Window.screen.width = CORE.Window.display.width;
  3333. CORE.Window.screen.height = CORE.Window.display.height;
  3334. }
  3335. const bool allowInterlaced = CORE.Window.flags & FLAG_INTERLACED_HINT;
  3336. const int fps = (CORE.Time.target > 0) ? (1.0/CORE.Time.target) : 60;
  3337. // try to find an exact matching mode
  3338. CORE.Window.modeIndex = FindExactConnectorMode(CORE.Window.connector, CORE.Window.screen.width, CORE.Window.screen.height, fps, allowInterlaced);
  3339. // if nothing found, try to find a nearly matching mode
  3340. if (CORE.Window.modeIndex < 0)
  3341. CORE.Window.modeIndex = FindNearestConnectorMode(CORE.Window.connector, CORE.Window.screen.width, CORE.Window.screen.height, fps, allowInterlaced);
  3342. // if nothing found, try to find an exactly matching mode including interlaced
  3343. if (CORE.Window.modeIndex < 0)
  3344. CORE.Window.modeIndex = FindExactConnectorMode(CORE.Window.connector, CORE.Window.screen.width, CORE.Window.screen.height, fps, true);
  3345. // if nothing found, try to find a nearly matching mode including interlaced
  3346. if (CORE.Window.modeIndex < 0)
  3347. CORE.Window.modeIndex = FindNearestConnectorMode(CORE.Window.connector, CORE.Window.screen.width, CORE.Window.screen.height, fps, true);
  3348. // if nothing found, there is no suitable mode
  3349. if (CORE.Window.modeIndex < 0)
  3350. {
  3351. TRACELOG(LOG_WARNING, "DISPLAY: Failed to find a suitable DRM connector mode");
  3352. drmModeFreeEncoder(enc);
  3353. drmModeFreeResources(res);
  3354. return false;
  3355. }
  3356. CORE.Window.display.width = CORE.Window.connector->modes[CORE.Window.modeIndex].hdisplay;
  3357. CORE.Window.display.height = CORE.Window.connector->modes[CORE.Window.modeIndex].vdisplay;
  3358. TRACELOG(LOG_INFO, "DISPLAY: Selected DRM connector mode %s (%ux%u%c@%u)", CORE.Window.connector->modes[CORE.Window.modeIndex].name,
  3359. CORE.Window.connector->modes[CORE.Window.modeIndex].hdisplay, CORE.Window.connector->modes[CORE.Window.modeIndex].vdisplay,
  3360. (CORE.Window.connector->modes[CORE.Window.modeIndex].flags & DRM_MODE_FLAG_INTERLACE) ? 'i' : 'p',
  3361. CORE.Window.connector->modes[CORE.Window.modeIndex].vrefresh);
  3362. // Use the width and height of the surface for render
  3363. CORE.Window.render.width = CORE.Window.screen.width;
  3364. CORE.Window.render.height = CORE.Window.screen.height;
  3365. drmModeFreeEncoder(enc);
  3366. enc = NULL;
  3367. drmModeFreeResources(res);
  3368. res = NULL;
  3369. CORE.Window.gbmDevice = gbm_create_device(CORE.Window.fd);
  3370. if (!CORE.Window.gbmDevice)
  3371. {
  3372. TRACELOG(LOG_WARNING, "DISPLAY: Failed to create GBM device");
  3373. return false;
  3374. }
  3375. CORE.Window.gbmSurface = gbm_surface_create(CORE.Window.gbmDevice, CORE.Window.connector->modes[CORE.Window.modeIndex].hdisplay,
  3376. CORE.Window.connector->modes[CORE.Window.modeIndex].vdisplay, GBM_FORMAT_ARGB8888, GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING);
  3377. if (!CORE.Window.gbmSurface)
  3378. {
  3379. TRACELOG(LOG_WARNING, "DISPLAY: Failed to create GBM surface");
  3380. return false;
  3381. }
  3382. #endif
  3383. EGLint samples = 0;
  3384. EGLint sampleBuffer = 0;
  3385. if (CORE.Window.flags & FLAG_MSAA_4X_HINT)
  3386. {
  3387. samples = 4;
  3388. sampleBuffer = 1;
  3389. TRACELOG(LOG_INFO, "DISPLAY: Trying to enable MSAA x4");
  3390. }
  3391. const EGLint framebufferAttribs[] =
  3392. {
  3393. EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, // Type of context support -> Required on RPI?
  3394. #if defined(PLATFORM_DRM)
  3395. EGL_SURFACE_TYPE, EGL_WINDOW_BIT, // Don't use it on Android!
  3396. #endif
  3397. EGL_RED_SIZE, 8, // RED color bit depth (alternative: 5)
  3398. EGL_GREEN_SIZE, 8, // GREEN color bit depth (alternative: 6)
  3399. EGL_BLUE_SIZE, 8, // BLUE color bit depth (alternative: 5)
  3400. #if defined(PLATFORM_DRM)
  3401. EGL_ALPHA_SIZE, 8, // ALPHA bit depth (required for transparent framebuffer)
  3402. #endif
  3403. //EGL_TRANSPARENT_TYPE, EGL_NONE, // Request transparent framebuffer (EGL_TRANSPARENT_RGB does not work on RPI)
  3404. EGL_DEPTH_SIZE, 16, // Depth buffer size (Required to use Depth testing!)
  3405. //EGL_STENCIL_SIZE, 8, // Stencil buffer size
  3406. EGL_SAMPLE_BUFFERS, sampleBuffer, // Activate MSAA
  3407. EGL_SAMPLES, samples, // 4x Antialiasing if activated (Free on MALI GPUs)
  3408. EGL_NONE
  3409. };
  3410. const EGLint contextAttribs[] =
  3411. {
  3412. EGL_CONTEXT_CLIENT_VERSION, 2,
  3413. EGL_NONE
  3414. };
  3415. #if defined(PLATFORM_ANDROID) || defined(PLATFORM_RPI) || defined(PLATFORM_DRM)
  3416. EGLint numConfigs = 0;
  3417. // Get an EGL device connection
  3418. #if defined(PLATFORM_DRM)
  3419. CORE.Window.device = eglGetDisplay((EGLNativeDisplayType)CORE.Window.gbmDevice);
  3420. #else
  3421. CORE.Window.device = eglGetDisplay(EGL_DEFAULT_DISPLAY);
  3422. #endif
  3423. if (CORE.Window.device == EGL_NO_DISPLAY)
  3424. {
  3425. TRACELOG(LOG_WARNING, "DISPLAY: Failed to initialize EGL device");
  3426. return false;
  3427. }
  3428. // Initialize the EGL device connection
  3429. if (eglInitialize(CORE.Window.device, NULL, NULL) == EGL_FALSE)
  3430. {
  3431. // If all of the calls to eglInitialize returned EGL_FALSE then an error has occurred.
  3432. TRACELOG(LOG_WARNING, "DISPLAY: Failed to initialize EGL device");
  3433. return false;
  3434. }
  3435. #if defined(PLATFORM_DRM)
  3436. if (!eglChooseConfig(CORE.Window.device, NULL, NULL, 0, &numConfigs))
  3437. {
  3438. TRACELOG(LOG_WARNING, "DISPLAY: Failed to get EGL config count: 0x%x", eglGetError());
  3439. return false;
  3440. }
  3441. TRACELOG(LOG_TRACE, "DISPLAY: EGL configs available: %d", numConfigs);
  3442. EGLConfig *configs = RL_CALLOC(numConfigs, sizeof(*configs));
  3443. if (!configs)
  3444. {
  3445. TRACELOG(LOG_WARNING, "DISPLAY: Failed to get memory for EGL configs");
  3446. return false;
  3447. }
  3448. EGLint matchingNumConfigs = 0;
  3449. if (!eglChooseConfig(CORE.Window.device, framebufferAttribs, configs, numConfigs, &matchingNumConfigs))
  3450. {
  3451. TRACELOG(LOG_WARNING, "DISPLAY: Failed to choose EGL config: 0x%x", eglGetError());
  3452. free(configs);
  3453. return false;
  3454. }
  3455. TRACELOG(LOG_TRACE, "DISPLAY: EGL matching configs available: %d", matchingNumConfigs);
  3456. // find the EGL config that matches the previously setup GBM format
  3457. int found = 0;
  3458. for (EGLint i = 0; i < matchingNumConfigs; ++i)
  3459. {
  3460. EGLint id = 0;
  3461. if (!eglGetConfigAttrib(CORE.Window.device, configs[i], EGL_NATIVE_VISUAL_ID, &id))
  3462. {
  3463. TRACELOG(LOG_WARNING, "DISPLAY: Failed to get EGL config attribute: 0x%x", eglGetError());
  3464. continue;
  3465. }
  3466. if (GBM_FORMAT_ARGB8888 == id)
  3467. {
  3468. TRACELOG(LOG_TRACE, "DISPLAY: Using EGL config: %d", i);
  3469. CORE.Window.config = configs[i];
  3470. found = 1;
  3471. break;
  3472. }
  3473. }
  3474. RL_FREE(configs);
  3475. if (!found)
  3476. {
  3477. TRACELOG(LOG_WARNING, "DISPLAY: Failed to find a suitable EGL config");
  3478. return false;
  3479. }
  3480. #else
  3481. // Get an appropriate EGL framebuffer configuration
  3482. eglChooseConfig(CORE.Window.device, framebufferAttribs, &CORE.Window.config, 1, &numConfigs);
  3483. #endif
  3484. // Set rendering API
  3485. eglBindAPI(EGL_OPENGL_ES_API);
  3486. // Create an EGL rendering context
  3487. CORE.Window.context = eglCreateContext(CORE.Window.device, CORE.Window.config, EGL_NO_CONTEXT, contextAttribs);
  3488. if (CORE.Window.context == EGL_NO_CONTEXT)
  3489. {
  3490. TRACELOG(LOG_WARNING, "DISPLAY: Failed to create EGL context");
  3491. return false;
  3492. }
  3493. #endif
  3494. // Create an EGL window surface
  3495. //---------------------------------------------------------------------------------
  3496. #if defined(PLATFORM_ANDROID)
  3497. EGLint displayFormat = 0;
  3498. // EGL_NATIVE_VISUAL_ID is an attribute of the EGLConfig that is guaranteed to be accepted by ANativeWindow_setBuffersGeometry()
  3499. // As soon as we picked a EGLConfig, we can safely reconfigure the ANativeWindow buffers to match, using EGL_NATIVE_VISUAL_ID
  3500. eglGetConfigAttrib(CORE.Window.device, CORE.Window.config, EGL_NATIVE_VISUAL_ID, &displayFormat);
  3501. // At this point we need to manage render size vs screen size
  3502. // NOTE: This function use and modify global module variables:
  3503. // -> CORE.Window.screen.width/CORE.Window.screen.height
  3504. // -> CORE.Window.render.width/CORE.Window.render.height
  3505. // -> CORE.Window.screenScale
  3506. SetupFramebuffer(CORE.Window.display.width, CORE.Window.display.height);
  3507. ANativeWindow_setBuffersGeometry(CORE.Android.app->window, CORE.Window.render.width, CORE.Window.render.height, displayFormat);
  3508. //ANativeWindow_setBuffersGeometry(CORE.Android.app->window, 0, 0, displayFormat); // Force use of native display size
  3509. CORE.Window.surface = eglCreateWindowSurface(CORE.Window.device, CORE.Window.config, CORE.Android.app->window, NULL);
  3510. #endif // PLATFORM_ANDROID
  3511. #if defined(PLATFORM_RPI)
  3512. graphics_get_display_size(0, &CORE.Window.display.width, &CORE.Window.display.height);
  3513. // Screen size security check
  3514. if (CORE.Window.screen.width <= 0) CORE.Window.screen.width = CORE.Window.display.width;
  3515. if (CORE.Window.screen.height <= 0) CORE.Window.screen.height = CORE.Window.display.height;
  3516. // At this point we need to manage render size vs screen size
  3517. // NOTE: This function use and modify global module variables:
  3518. // -> CORE.Window.screen.width/CORE.Window.screen.height
  3519. // -> CORE.Window.render.width/CORE.Window.render.height
  3520. // -> CORE.Window.screenScale
  3521. SetupFramebuffer(CORE.Window.display.width, CORE.Window.display.height);
  3522. dstRect.x = 0;
  3523. dstRect.y = 0;
  3524. dstRect.width = CORE.Window.display.width;
  3525. dstRect.height = CORE.Window.display.height;
  3526. srcRect.x = 0;
  3527. srcRect.y = 0;
  3528. srcRect.width = CORE.Window.render.width << 16;
  3529. srcRect.height = CORE.Window.render.height << 16;
  3530. // NOTE: RPI dispmanx windowing system takes care of source rectangle scaling to destination rectangle by hardware (no cost)
  3531. // Take care that renderWidth/renderHeight fit on displayWidth/displayHeight aspect ratio
  3532. VC_DISPMANX_ALPHA_T alpha = { 0 };
  3533. alpha.flags = DISPMANX_FLAGS_ALPHA_FIXED_ALL_PIXELS;
  3534. //alpha.flags = DISPMANX_FLAGS_ALPHA_FROM_SOURCE; // TODO: Allow transparent framebuffer! -> FLAG_WINDOW_TRANSPARENT
  3535. alpha.opacity = 255; // Set transparency level for framebuffer, requires EGLAttrib: EGL_TRANSPARENT_TYPE
  3536. alpha.mask = 0;
  3537. dispmanDisplay = vc_dispmanx_display_open(0); // LCD
  3538. dispmanUpdate = vc_dispmanx_update_start(0);
  3539. dispmanElement = vc_dispmanx_element_add(dispmanUpdate, dispmanDisplay, 0/*layer*/, &dstRect, 0/*src*/,
  3540. &srcRect, DISPMANX_PROTECTION_NONE, &alpha, 0/*clamp*/, DISPMANX_NO_ROTATE);
  3541. CORE.Window.handle.element = dispmanElement;
  3542. CORE.Window.handle.width = CORE.Window.render.width;
  3543. CORE.Window.handle.height = CORE.Window.render.height;
  3544. vc_dispmanx_update_submit_sync(dispmanUpdate);
  3545. CORE.Window.surface = eglCreateWindowSurface(CORE.Window.device, CORE.Window.config, &CORE.Window.handle, NULL);
  3546. const unsigned char *const renderer = glGetString(GL_RENDERER);
  3547. if (renderer) TRACELOG(LOG_INFO, "DISPLAY: Renderer name is: %s", renderer);
  3548. else TRACELOG(LOG_WARNING, "DISPLAY: Failed to get renderer name");
  3549. //---------------------------------------------------------------------------------
  3550. #endif // PLATFORM_RPI
  3551. #if defined(PLATFORM_DRM)
  3552. CORE.Window.surface = eglCreateWindowSurface(CORE.Window.device, CORE.Window.config, (EGLNativeWindowType)CORE.Window.gbmSurface, NULL);
  3553. if (EGL_NO_SURFACE == CORE.Window.surface)
  3554. {
  3555. TRACELOG(LOG_WARNING, "DISPLAY: Failed to create EGL window surface: 0x%04x", eglGetError());
  3556. return false;
  3557. }
  3558. // At this point we need to manage render size vs screen size
  3559. // NOTE: This function use and modify global module variables:
  3560. // -> CORE.Window.screen.width/CORE.Window.screen.height
  3561. // -> CORE.Window.render.width/CORE.Window.render.height
  3562. // -> CORE.Window.screenScale
  3563. SetupFramebuffer(CORE.Window.display.width, CORE.Window.display.height);
  3564. #endif // PLATFORM_DRM
  3565. // There must be at least one frame displayed before the buffers are swapped
  3566. //eglSwapInterval(CORE.Window.device, 1);
  3567. if (eglMakeCurrent(CORE.Window.device, CORE.Window.surface, CORE.Window.surface, CORE.Window.context) == EGL_FALSE)
  3568. {
  3569. TRACELOG(LOG_WARNING, "DISPLAY: Failed to attach EGL rendering context to EGL surface");
  3570. return false;
  3571. }
  3572. else
  3573. {
  3574. TRACELOG(LOG_INFO, "DISPLAY: Device initialized successfully");
  3575. TRACELOG(LOG_INFO, " > Display size: %i x %i", CORE.Window.display.width, CORE.Window.display.height);
  3576. TRACELOG(LOG_INFO, " > Render size: %i x %i", CORE.Window.render.width, CORE.Window.render.height);
  3577. TRACELOG(LOG_INFO, " > Screen size: %i x %i", CORE.Window.screen.width, CORE.Window.screen.height);
  3578. TRACELOG(LOG_INFO, " > Viewport offsets: %i, %i", CORE.Window.renderOffset.x, CORE.Window.renderOffset.y);
  3579. }
  3580. #endif // PLATFORM_ANDROID || PLATFORM_RPI || PLATFORM_DRM
  3581. // Load OpenGL extensions
  3582. // NOTE: GL procedures address loader is required to load extensions
  3583. #if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB)
  3584. rlLoadExtensions(glfwGetProcAddress);
  3585. #else
  3586. rlLoadExtensions(eglGetProcAddress);
  3587. #endif
  3588. // Initialize OpenGL context (states and resources)
  3589. // NOTE: CORE.Window.screen.width and CORE.Window.screen.height not used, just stored as globals in rlgl
  3590. rlglInit(CORE.Window.screen.width, CORE.Window.screen.height);
  3591. int fbWidth = CORE.Window.render.width;
  3592. int fbHeight = CORE.Window.render.height;
  3593. #if defined(PLATFORM_DESKTOP)
  3594. if ((CORE.Window.flags & FLAG_WINDOW_HIGHDPI) > 0)
  3595. {
  3596. // NOTE: On APPLE platforms system should manage window/input scaling and also framebuffer scaling
  3597. // Framebuffer scaling should be activated with: glfwWindowHint(GLFW_COCOA_RETINA_FRAMEBUFFER, GLFW_TRUE);
  3598. #if !defined(__APPLE__)
  3599. glfwGetFramebufferSize(CORE.Window.handle, &fbWidth, &fbHeight);
  3600. // Screen scaling matrix is required in case desired screen area is different than display area
  3601. CORE.Window.screenScale = MatrixScale((float)fbWidth/CORE.Window.screen.width, (float)fbHeight/CORE.Window.screen.height, 1.0f);
  3602. // Mouse input scaling for the new screen size
  3603. SetMouseScale((float)CORE.Window.screen.width/fbWidth, (float)CORE.Window.screen.height/fbHeight);
  3604. #endif
  3605. }
  3606. #endif
  3607. // Setup default viewport
  3608. SetupViewport(fbWidth, fbHeight);
  3609. CORE.Window.currentFbo.width = CORE.Window.screen.width;
  3610. CORE.Window.currentFbo.height = CORE.Window.screen.height;
  3611. ClearBackground(RAYWHITE); // Default background color for raylib games :P
  3612. #if defined(PLATFORM_ANDROID)
  3613. CORE.Window.ready = true;
  3614. #endif
  3615. if ((CORE.Window.flags & FLAG_WINDOW_MINIMIZED) > 0) MinimizeWindow();
  3616. return true;
  3617. }
  3618. // Set viewport for a provided width and height
  3619. static void SetupViewport(int width, int height)
  3620. {
  3621. CORE.Window.render.width = width;
  3622. CORE.Window.render.height = height;
  3623. // Set viewport width and height
  3624. // NOTE: We consider render size (scaled) and offset in case black bars are required and
  3625. // render area does not match full display area (this situation is only applicable on fullscreen mode)
  3626. #if defined(__APPLE__)
  3627. float xScale = 1.0f, yScale = 1.0f;
  3628. glfwGetWindowContentScale(CORE.Window.handle, &xScale, &yScale);
  3629. rlViewport(CORE.Window.renderOffset.x/2*xScale, CORE.Window.renderOffset.y/2*yScale, (CORE.Window.render.width - CORE.Window.renderOffset.x)*xScale, (CORE.Window.render.height - CORE.Window.renderOffset.y)*yScale);
  3630. #else
  3631. rlViewport(CORE.Window.renderOffset.x/2, CORE.Window.renderOffset.y/2, CORE.Window.render.width - CORE.Window.renderOffset.x, CORE.Window.render.height - CORE.Window.renderOffset.y);
  3632. #endif
  3633. rlMatrixMode(RL_PROJECTION); // Switch to projection matrix
  3634. rlLoadIdentity(); // Reset current matrix (projection)
  3635. // Set orthographic projection to current framebuffer size
  3636. // NOTE: Configured top-left corner as (0, 0)
  3637. rlOrtho(0, CORE.Window.render.width, CORE.Window.render.height, 0, 0.0f, 1.0f);
  3638. rlMatrixMode(RL_MODELVIEW); // Switch back to modelview matrix
  3639. rlLoadIdentity(); // Reset current matrix (modelview)
  3640. }
  3641. // Compute framebuffer size relative to screen size and display size
  3642. // NOTE: Global variables CORE.Window.render.width/CORE.Window.render.height and CORE.Window.renderOffset.x/CORE.Window.renderOffset.y can be modified
  3643. static void SetupFramebuffer(int width, int height)
  3644. {
  3645. // Calculate CORE.Window.render.width and CORE.Window.render.height, we have the display size (input params) and the desired screen size (global var)
  3646. if ((CORE.Window.screen.width > CORE.Window.display.width) || (CORE.Window.screen.height > CORE.Window.display.height))
  3647. {
  3648. TRACELOG(LOG_WARNING, "DISPLAY: Downscaling required: Screen size (%ix%i) is bigger than display size (%ix%i)", CORE.Window.screen.width, CORE.Window.screen.height, CORE.Window.display.width, CORE.Window.display.height);
  3649. // Downscaling to fit display with border-bars
  3650. float widthRatio = (float)CORE.Window.display.width/(float)CORE.Window.screen.width;
  3651. float heightRatio = (float)CORE.Window.display.height/(float)CORE.Window.screen.height;
  3652. if (widthRatio <= heightRatio)
  3653. {
  3654. CORE.Window.render.width = CORE.Window.display.width;
  3655. CORE.Window.render.height = (int)round((float)CORE.Window.screen.height*widthRatio);
  3656. CORE.Window.renderOffset.x = 0;
  3657. CORE.Window.renderOffset.y = (CORE.Window.display.height - CORE.Window.render.height);
  3658. }
  3659. else
  3660. {
  3661. CORE.Window.render.width = (int)round((float)CORE.Window.screen.width*heightRatio);
  3662. CORE.Window.render.height = CORE.Window.display.height;
  3663. CORE.Window.renderOffset.x = (CORE.Window.display.width - CORE.Window.render.width);
  3664. CORE.Window.renderOffset.y = 0;
  3665. }
  3666. // Screen scaling required
  3667. float scaleRatio = (float)CORE.Window.render.width/(float)CORE.Window.screen.width;
  3668. CORE.Window.screenScale = MatrixScale(scaleRatio, scaleRatio, 1.0f);
  3669. // NOTE: We render to full display resolution!
  3670. // We just need to calculate above parameters for downscale matrix and offsets
  3671. CORE.Window.render.width = CORE.Window.display.width;
  3672. CORE.Window.render.height = CORE.Window.display.height;
  3673. TRACELOG(LOG_WARNING, "DISPLAY: Downscale matrix generated, content will be rendered at (%ix%i)", CORE.Window.render.width, CORE.Window.render.height);
  3674. }
  3675. else if ((CORE.Window.screen.width < CORE.Window.display.width) || (CORE.Window.screen.height < CORE.Window.display.height))
  3676. {
  3677. // Required screen size is smaller than display size
  3678. TRACELOG(LOG_INFO, "DISPLAY: Upscaling required: Screen size (%ix%i) smaller than display size (%ix%i)", CORE.Window.screen.width, CORE.Window.screen.height, CORE.Window.display.width, CORE.Window.display.height);
  3679. if ((CORE.Window.screen.width == 0) || (CORE.Window.screen.height == 0))
  3680. {
  3681. CORE.Window.screen.width = CORE.Window.display.width;
  3682. CORE.Window.screen.height = CORE.Window.display.height;
  3683. }
  3684. // Upscaling to fit display with border-bars
  3685. float displayRatio = (float)CORE.Window.display.width/(float)CORE.Window.display.height;
  3686. float screenRatio = (float)CORE.Window.screen.width/(float)CORE.Window.screen.height;
  3687. if (displayRatio <= screenRatio)
  3688. {
  3689. CORE.Window.render.width = CORE.Window.screen.width;
  3690. CORE.Window.render.height = (int)round((float)CORE.Window.screen.width/displayRatio);
  3691. CORE.Window.renderOffset.x = 0;
  3692. CORE.Window.renderOffset.y = (CORE.Window.render.height - CORE.Window.screen.height);
  3693. }
  3694. else
  3695. {
  3696. CORE.Window.render.width = (int)round((float)CORE.Window.screen.height*displayRatio);
  3697. CORE.Window.render.height = CORE.Window.screen.height;
  3698. CORE.Window.renderOffset.x = (CORE.Window.render.width - CORE.Window.screen.width);
  3699. CORE.Window.renderOffset.y = 0;
  3700. }
  3701. }
  3702. else
  3703. {
  3704. CORE.Window.render.width = CORE.Window.screen.width;
  3705. CORE.Window.render.height = CORE.Window.screen.height;
  3706. CORE.Window.renderOffset.x = 0;
  3707. CORE.Window.renderOffset.y = 0;
  3708. }
  3709. }
  3710. // Initialize hi-resolution timer
  3711. static void InitTimer(void)
  3712. {
  3713. // Setting a higher resolution can improve the accuracy of time-out intervals in wait functions.
  3714. // However, it can also reduce overall system performance, because the thread scheduler switches tasks more often.
  3715. // High resolutions can also prevent the CPU power management system from entering power-saving modes.
  3716. // Setting a higher resolution does not improve the accuracy of the high-resolution performance counter.
  3717. #if defined(_WIN32) && defined(SUPPORT_WINMM_HIGHRES_TIMER) && !defined(SUPPORT_BUSY_WAIT_LOOP)
  3718. timeBeginPeriod(1); // Setup high-resolution timer to 1ms (granularity of 1-2 ms)
  3719. #endif
  3720. #if defined(PLATFORM_ANDROID) || defined(PLATFORM_RPI) || defined(PLATFORM_DRM)
  3721. struct timespec now = { 0 };
  3722. if (clock_gettime(CLOCK_MONOTONIC, &now) == 0) // Success
  3723. {
  3724. CORE.Time.base = (unsigned long long int)now.tv_sec*1000000000LLU + (unsigned long long int)now.tv_nsec;
  3725. }
  3726. else TRACELOG(LOG_WARNING, "TIMER: Hi-resolution timer not available");
  3727. #endif
  3728. CORE.Time.previous = GetTime(); // Get time as double
  3729. }
  3730. // Wait for some milliseconds (stop program execution)
  3731. // NOTE: Sleep() granularity could be around 10 ms, it means, Sleep() could
  3732. // take longer than expected... for that reason we use the busy wait loop
  3733. // Ref: http://stackoverflow.com/questions/43057578/c-programming-win32-games-sleep-taking-longer-than-expected
  3734. // Ref: http://www.geisswerks.com/ryan/FAQS/timing.html --> All about timming on Win32!
  3735. void WaitTime(float ms)
  3736. {
  3737. #if defined(SUPPORT_BUSY_WAIT_LOOP)
  3738. double previousTime = GetTime();
  3739. double currentTime = 0.0;
  3740. // Busy wait loop
  3741. while ((currentTime - previousTime) < ms/1000.0f) currentTime = GetTime();
  3742. #else
  3743. #if defined(SUPPORT_PARTIALBUSY_WAIT_LOOP)
  3744. double busyWait = ms*0.05; // NOTE: We are using a busy wait of 5% of the time
  3745. ms -= (float)busyWait;
  3746. #endif
  3747. // System halt functions
  3748. #if defined(_WIN32)
  3749. Sleep((unsigned int)ms);
  3750. #endif
  3751. #if defined(__linux__) || defined(__FreeBSD__) || defined(__EMSCRIPTEN__)
  3752. struct timespec req = { 0 };
  3753. time_t sec = (int)(ms/1000.0f);
  3754. ms -= (sec*1000);
  3755. req.tv_sec = sec;
  3756. req.tv_nsec = ms*1000000L;
  3757. // NOTE: Use nanosleep() on Unix platforms... usleep() it's deprecated.
  3758. while (nanosleep(&req, &req) == -1) continue;
  3759. #endif
  3760. #if defined(__APPLE__)
  3761. usleep(ms*1000.0f);
  3762. #endif
  3763. #if defined(SUPPORT_PARTIALBUSY_WAIT_LOOP)
  3764. double previousTime = GetTime();
  3765. double currentTime = 0.0;
  3766. // Partial busy wait loop (only a fraction of the total wait time)
  3767. while ((currentTime - previousTime) < busyWait/1000.0f) currentTime = GetTime();
  3768. #endif
  3769. #endif
  3770. }
  3771. // Swap back buffer with front buffer (screen drawing)
  3772. void SwapScreenBuffer(void)
  3773. {
  3774. #if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB)
  3775. glfwSwapBuffers(CORE.Window.handle);
  3776. #endif
  3777. #if defined(PLATFORM_ANDROID) || defined(PLATFORM_RPI) || defined(PLATFORM_DRM)
  3778. eglSwapBuffers(CORE.Window.device, CORE.Window.surface);
  3779. #if defined(PLATFORM_DRM)
  3780. if (!CORE.Window.gbmSurface || (-1 == CORE.Window.fd) || !CORE.Window.connector || !CORE.Window.crtc)
  3781. {
  3782. TRACELOG(LOG_ERROR, "DISPLAY: DRM initialization failed to swap");
  3783. abort();
  3784. }
  3785. struct gbm_bo *bo = gbm_surface_lock_front_buffer(CORE.Window.gbmSurface);
  3786. if (!bo)
  3787. {
  3788. TRACELOG(LOG_ERROR, "DISPLAY: Failed GBM to lock front buffer");
  3789. abort();
  3790. }
  3791. uint32_t fb = 0;
  3792. int result = drmModeAddFB(CORE.Window.fd, CORE.Window.connector->modes[CORE.Window.modeIndex].hdisplay,
  3793. CORE.Window.connector->modes[CORE.Window.modeIndex].vdisplay, 24, 32, gbm_bo_get_stride(bo), gbm_bo_get_handle(bo).u32, &fb);
  3794. if (0 != result)
  3795. {
  3796. TRACELOG(LOG_ERROR, "DISPLAY: drmModeAddFB() failed with result: %d", result);
  3797. abort();
  3798. }
  3799. result = drmModeSetCrtc(CORE.Window.fd, CORE.Window.crtc->crtc_id, fb, 0, 0,
  3800. &CORE.Window.connector->connector_id, 1, &CORE.Window.connector->modes[CORE.Window.modeIndex]);
  3801. if (0 != result)
  3802. {
  3803. TRACELOG(LOG_ERROR, "DISPLAY: drmModeSetCrtc() failed with result: %d", result);
  3804. abort();
  3805. }
  3806. if (CORE.Window.prevFB)
  3807. {
  3808. result = drmModeRmFB(CORE.Window.fd, CORE.Window.prevFB);
  3809. if (0 != result)
  3810. {
  3811. TRACELOG(LOG_ERROR, "DISPLAY: drmModeRmFB() failed with result: %d", result);
  3812. abort();
  3813. }
  3814. }
  3815. CORE.Window.prevFB = fb;
  3816. if (CORE.Window.prevBO)
  3817. {
  3818. gbm_surface_release_buffer(CORE.Window.gbmSurface, CORE.Window.prevBO);
  3819. }
  3820. CORE.Window.prevBO = bo;
  3821. #endif // PLATFORM_DRM
  3822. #endif // PLATFORM_ANDROID || PLATFORM_RPI || PLATFORM_DRM
  3823. }
  3824. // Register all input events
  3825. void PollInputEvents(void)
  3826. {
  3827. #if defined(SUPPORT_GESTURES_SYSTEM)
  3828. // NOTE: Gestures update must be called every frame to reset gestures correctly
  3829. // because ProcessGestureEvent() is just called on an event, not every frame
  3830. UpdateGestures();
  3831. #endif
  3832. // Reset keys/chars pressed registered
  3833. CORE.Input.Keyboard.keyPressedQueueCount = 0;
  3834. CORE.Input.Keyboard.charPressedQueueCount = 0;
  3835. #if !(defined(PLATFORM_RPI) || defined(PLATFORM_DRM))
  3836. // Reset last gamepad button/axis registered state
  3837. CORE.Input.Gamepad.lastButtonPressed = -1;
  3838. CORE.Input.Gamepad.axisCount = 0;
  3839. #endif
  3840. #if defined(PLATFORM_RPI) || defined(PLATFORM_DRM)
  3841. // Register previous keys states
  3842. for (int i = 0; i < MAX_KEYBOARD_KEYS; i++) CORE.Input.Keyboard.previousKeyState[i] = CORE.Input.Keyboard.currentKeyState[i];
  3843. PollKeyboardEvents();
  3844. // Register previous mouse states
  3845. CORE.Input.Mouse.previousWheelMove = CORE.Input.Mouse.currentWheelMove;
  3846. CORE.Input.Mouse.currentWheelMove = 0.0f;
  3847. for (int i = 0; i < 3; i++)
  3848. {
  3849. CORE.Input.Mouse.previousButtonState[i] = CORE.Input.Mouse.currentButtonState[i];
  3850. CORE.Input.Mouse.currentButtonState[i] = CORE.Input.Mouse.currentButtonStateEvdev[i];
  3851. }
  3852. // Register gamepads buttons events
  3853. for (int i = 0; i < MAX_GAMEPADS; i++)
  3854. {
  3855. if (CORE.Input.Gamepad.ready[i])
  3856. {
  3857. // Register previous gamepad states
  3858. for (int k = 0; k < MAX_GAMEPAD_BUTTONS; k++) CORE.Input.Gamepad.previousButtonState[i][k] = CORE.Input.Gamepad.currentButtonState[i][k];
  3859. }
  3860. }
  3861. #endif
  3862. #if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB)
  3863. // Keyboard/Mouse input polling (automatically managed by GLFW3 through callback)
  3864. // Register previous keys states
  3865. for (int i = 0; i < MAX_KEYBOARD_KEYS; i++) CORE.Input.Keyboard.previousKeyState[i] = CORE.Input.Keyboard.currentKeyState[i];
  3866. // Register previous mouse states
  3867. for (int i = 0; i < 3; i++) CORE.Input.Mouse.previousButtonState[i] = CORE.Input.Mouse.currentButtonState[i];
  3868. // Register previous mouse wheel state
  3869. CORE.Input.Mouse.previousWheelMove = CORE.Input.Mouse.currentWheelMove;
  3870. CORE.Input.Mouse.currentWheelMove = 0.0f;
  3871. // Register previous mouse position
  3872. CORE.Input.Mouse.previousPosition = CORE.Input.Mouse.currentPosition;
  3873. #endif
  3874. // Register previous touch states
  3875. for (int i = 0; i < MAX_TOUCH_POINTS; i++) CORE.Input.Touch.previousTouchState[i] = CORE.Input.Touch.currentTouchState[i];
  3876. #if defined(PLATFORM_DESKTOP)
  3877. // Check if gamepads are ready
  3878. // NOTE: We do it here in case of disconnection
  3879. for (int i = 0; i < MAX_GAMEPADS; i++)
  3880. {
  3881. if (glfwJoystickPresent(i)) CORE.Input.Gamepad.ready[i] = true;
  3882. else CORE.Input.Gamepad.ready[i] = false;
  3883. }
  3884. // Register gamepads buttons events
  3885. for (int i = 0; i < MAX_GAMEPADS; i++)
  3886. {
  3887. if (CORE.Input.Gamepad.ready[i]) // Check if gamepad is available
  3888. {
  3889. // Register previous gamepad states
  3890. for (int k = 0; k < MAX_GAMEPAD_BUTTONS; k++) CORE.Input.Gamepad.previousButtonState[i][k] = CORE.Input.Gamepad.currentButtonState[i][k];
  3891. // Get current gamepad state
  3892. // NOTE: There is no callback available, so we get it manually
  3893. // Get remapped buttons
  3894. GLFWgamepadstate state = { 0 };
  3895. glfwGetGamepadState(i, &state); // This remapps all gamepads so they have their buttons mapped like an xbox controller
  3896. const unsigned char *buttons = state.buttons;
  3897. for (int k = 0; (buttons != NULL) && (k < GLFW_GAMEPAD_BUTTON_DPAD_LEFT + 1) && (k < MAX_GAMEPAD_BUTTONS); k++)
  3898. {
  3899. GamepadButton button = -1;
  3900. switch (k)
  3901. {
  3902. case GLFW_GAMEPAD_BUTTON_Y: button = GAMEPAD_BUTTON_RIGHT_FACE_UP; break;
  3903. case GLFW_GAMEPAD_BUTTON_B: button = GAMEPAD_BUTTON_RIGHT_FACE_RIGHT; break;
  3904. case GLFW_GAMEPAD_BUTTON_A: button = GAMEPAD_BUTTON_RIGHT_FACE_DOWN; break;
  3905. case GLFW_GAMEPAD_BUTTON_X: button = GAMEPAD_BUTTON_RIGHT_FACE_LEFT; break;
  3906. case GLFW_GAMEPAD_BUTTON_LEFT_BUMPER: button = GAMEPAD_BUTTON_LEFT_TRIGGER_1; break;
  3907. case GLFW_GAMEPAD_BUTTON_RIGHT_BUMPER: button = GAMEPAD_BUTTON_RIGHT_TRIGGER_1; break;
  3908. case GLFW_GAMEPAD_BUTTON_BACK: button = GAMEPAD_BUTTON_MIDDLE_LEFT; break;
  3909. case GLFW_GAMEPAD_BUTTON_GUIDE: button = GAMEPAD_BUTTON_MIDDLE; break;
  3910. case GLFW_GAMEPAD_BUTTON_START: button = GAMEPAD_BUTTON_MIDDLE_RIGHT; break;
  3911. case GLFW_GAMEPAD_BUTTON_DPAD_UP: button = GAMEPAD_BUTTON_LEFT_FACE_UP; break;
  3912. case GLFW_GAMEPAD_BUTTON_DPAD_RIGHT: button = GAMEPAD_BUTTON_LEFT_FACE_RIGHT; break;
  3913. case GLFW_GAMEPAD_BUTTON_DPAD_DOWN: button = GAMEPAD_BUTTON_LEFT_FACE_DOWN; break;
  3914. case GLFW_GAMEPAD_BUTTON_DPAD_LEFT: button = GAMEPAD_BUTTON_LEFT_FACE_LEFT; break;
  3915. case GLFW_GAMEPAD_BUTTON_LEFT_THUMB: button = GAMEPAD_BUTTON_LEFT_THUMB; break;
  3916. case GLFW_GAMEPAD_BUTTON_RIGHT_THUMB: button = GAMEPAD_BUTTON_RIGHT_THUMB; break;
  3917. default: break;
  3918. }
  3919. if (button != -1) // Check for valid button
  3920. {
  3921. if (buttons[k] == GLFW_PRESS)
  3922. {
  3923. CORE.Input.Gamepad.currentButtonState[i][button] = 1;
  3924. CORE.Input.Gamepad.lastButtonPressed = button;
  3925. }
  3926. else CORE.Input.Gamepad.currentButtonState[i][button] = 0;
  3927. }
  3928. }
  3929. // Get current axis state
  3930. const float *axes = state.axes;
  3931. for (int k = 0; (axes != NULL) && (k < GLFW_GAMEPAD_AXIS_LAST + 1) && (k < MAX_GAMEPAD_AXIS); k++)
  3932. {
  3933. CORE.Input.Gamepad.axisState[i][k] = axes[k];
  3934. }
  3935. // Register buttons for 2nd triggers (because GLFW doesn't count these as buttons but rather axis)
  3936. CORE.Input.Gamepad.currentButtonState[i][GAMEPAD_BUTTON_LEFT_TRIGGER_2] = (char)(CORE.Input.Gamepad.axisState[i][GAMEPAD_AXIS_LEFT_TRIGGER] > 0.1);
  3937. CORE.Input.Gamepad.currentButtonState[i][GAMEPAD_BUTTON_RIGHT_TRIGGER_2] = (char)(CORE.Input.Gamepad.axisState[i][GAMEPAD_AXIS_RIGHT_TRIGGER] > 0.1);
  3938. CORE.Input.Gamepad.axisCount = GLFW_GAMEPAD_AXIS_LAST + 1;
  3939. }
  3940. }
  3941. CORE.Window.resizedLastFrame = false;
  3942. #if defined(SUPPORT_EVENTS_WAITING)
  3943. glfwWaitEvents();
  3944. #else
  3945. glfwPollEvents(); // Register keyboard/mouse events (callbacks)... and window events!
  3946. #endif
  3947. #endif // PLATFORM_DESKTOP
  3948. // Gamepad support using emscripten API
  3949. // NOTE: GLFW3 joystick functionality not available in web
  3950. #if defined(PLATFORM_WEB)
  3951. // Get number of gamepads connected
  3952. int numGamepads = 0;
  3953. if (emscripten_sample_gamepad_data() == EMSCRIPTEN_RESULT_SUCCESS) numGamepads = emscripten_get_num_gamepads();
  3954. for (int i = 0; (i < numGamepads) && (i < MAX_GAMEPADS); i++)
  3955. {
  3956. // Register previous gamepad button states
  3957. for (int k = 0; k < MAX_GAMEPAD_BUTTONS; k++) CORE.Input.Gamepad.previousButtonState[i][k] = CORE.Input.Gamepad.currentButtonState[i][k];
  3958. EmscriptenGamepadEvent gamepadState;
  3959. int result = emscripten_get_gamepad_status(i, &gamepadState);
  3960. if (result == EMSCRIPTEN_RESULT_SUCCESS)
  3961. {
  3962. // Register buttons data for every connected gamepad
  3963. for (int j = 0; (j < gamepadState.numButtons) && (j < MAX_GAMEPAD_BUTTONS); j++)
  3964. {
  3965. GamepadButton button = -1;
  3966. // Gamepad Buttons reference: https://www.w3.org/TR/gamepad/#gamepad-interface
  3967. switch (j)
  3968. {
  3969. case 0: button = GAMEPAD_BUTTON_RIGHT_FACE_DOWN; break;
  3970. case 1: button = GAMEPAD_BUTTON_RIGHT_FACE_RIGHT; break;
  3971. case 2: button = GAMEPAD_BUTTON_RIGHT_FACE_LEFT; break;
  3972. case 3: button = GAMEPAD_BUTTON_RIGHT_FACE_UP; break;
  3973. case 4: button = GAMEPAD_BUTTON_LEFT_TRIGGER_1; break;
  3974. case 5: button = GAMEPAD_BUTTON_RIGHT_TRIGGER_1; break;
  3975. case 6: button = GAMEPAD_BUTTON_LEFT_TRIGGER_2; break;
  3976. case 7: button = GAMEPAD_BUTTON_RIGHT_TRIGGER_2; break;
  3977. case 8: button = GAMEPAD_BUTTON_MIDDLE_LEFT; break;
  3978. case 9: button = GAMEPAD_BUTTON_MIDDLE_RIGHT; break;
  3979. case 10: button = GAMEPAD_BUTTON_LEFT_THUMB; break;
  3980. case 11: button = GAMEPAD_BUTTON_RIGHT_THUMB; break;
  3981. case 12: button = GAMEPAD_BUTTON_LEFT_FACE_UP; break;
  3982. case 13: button = GAMEPAD_BUTTON_LEFT_FACE_DOWN; break;
  3983. case 14: button = GAMEPAD_BUTTON_LEFT_FACE_LEFT; break;
  3984. case 15: button = GAMEPAD_BUTTON_LEFT_FACE_RIGHT; break;
  3985. default: break;
  3986. }
  3987. if (button != -1) // Check for valid button
  3988. {
  3989. if (gamepadState.digitalButton[j] == 1)
  3990. {
  3991. CORE.Input.Gamepad.currentButtonState[i][button] = 1;
  3992. CORE.Input.Gamepad.lastButtonPressed = button;
  3993. }
  3994. else CORE.Input.Gamepad.currentButtonState[i][button] = 0;
  3995. }
  3996. //TRACELOGD("INPUT: Gamepad %d, button %d: Digital: %d, Analog: %g", gamepadState.index, j, gamepadState.digitalButton[j], gamepadState.analogButton[j]);
  3997. }
  3998. // Register axis data for every connected gamepad
  3999. for (int j = 0; (j < gamepadState.numAxes) && (j < MAX_GAMEPAD_AXIS); j++)
  4000. {
  4001. CORE.Input.Gamepad.axisState[i][j] = gamepadState.axis[j];
  4002. }
  4003. CORE.Input.Gamepad.axisCount = gamepadState.numAxes;
  4004. }
  4005. }
  4006. #endif
  4007. #if defined(PLATFORM_ANDROID)
  4008. // Register previous keys states
  4009. // NOTE: Android supports up to 260 keys
  4010. for (int i = 0; i < 260; i++) CORE.Input.Keyboard.previousKeyState[i] = CORE.Input.Keyboard.currentKeyState[i];
  4011. // Android ALooper_pollAll() variables
  4012. int pollResult = 0;
  4013. int pollEvents = 0;
  4014. // Poll Events (registered events)
  4015. // NOTE: Activity is paused if not enabled (CORE.Android.appEnabled)
  4016. while ((pollResult = ALooper_pollAll(CORE.Android.appEnabled? 0 : -1, NULL, &pollEvents, (void**)&CORE.Android.source)) >= 0)
  4017. {
  4018. // Process this event
  4019. if (CORE.Android.source != NULL) CORE.Android.source->process(CORE.Android.app, CORE.Android.source);
  4020. // NOTE: Never close window, native activity is controlled by the system!
  4021. if (CORE.Android.app->destroyRequested != 0)
  4022. {
  4023. //CORE.Window.shouldClose = true;
  4024. //ANativeActivity_finish(CORE.Android.app->activity);
  4025. }
  4026. }
  4027. #endif
  4028. #if (defined(PLATFORM_RPI) || defined(PLATFORM_DRM)) && defined(SUPPORT_SSH_KEYBOARD_RPI)
  4029. // NOTE: Keyboard reading could be done using input_event(s) or just read from stdin, both methods are used here.
  4030. // stdin reading is still used for legacy purposes, it allows keyboard input trough SSH console
  4031. if (!CORE.Input.Keyboard.evtMode) ProcessKeyboard();
  4032. // NOTE: Mouse input events polling is done asynchronously in another pthread - EventThread()
  4033. // NOTE: Gamepad (Joystick) input events polling is done asynchonously in another pthread - GamepadThread()
  4034. #endif
  4035. }
  4036. #if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB)
  4037. // GLFW3 Error Callback, runs on GLFW3 error
  4038. static void ErrorCallback(int error, const char *description)
  4039. {
  4040. TRACELOG(LOG_WARNING, "GLFW: Error: %i Description: %s", error, description);
  4041. }
  4042. #if defined(PLATFORM_WEB)
  4043. EM_JS(int, GetCanvasWidth, (), { return canvas.clientWidth; });
  4044. EM_JS(int, GetCanvasHeight, (), { return canvas.clientHeight; });
  4045. static EM_BOOL EmscriptenResizeCallback(int eventType, const EmscriptenUiEvent *e, void *userData)
  4046. {
  4047. // Don't resize non-resizeable windows
  4048. if ((CORE.Window.flags & FLAG_WINDOW_RESIZABLE) == 0) return 1;
  4049. // This event is called whenever the window changes sizes,
  4050. // so the size of the canvas object is explicitly retrieved below
  4051. int width = GetCanvasWidth();
  4052. int height = GetCanvasHeight();
  4053. emscripten_set_canvas_element_size("#canvas",width,height);
  4054. SetupViewport(width, height); // Reset viewport and projection matrix for new size
  4055. CORE.Window.currentFbo.width = width;
  4056. CORE.Window.currentFbo.height = height;
  4057. CORE.Window.resizedLastFrame = true;
  4058. if (IsWindowFullscreen()) return 1;
  4059. // Set current screen size
  4060. CORE.Window.screen.width = width;
  4061. CORE.Window.screen.height = height;
  4062. // NOTE: Postprocessing texture is not scaled to new size
  4063. return 0;
  4064. }
  4065. #endif
  4066. // GLFW3 WindowSize Callback, runs when window is resizedLastFrame
  4067. // NOTE: Window resizing not allowed by default
  4068. static void WindowSizeCallback(GLFWwindow *window, int width, int height)
  4069. {
  4070. // Reset viewport and projection matrix for new size
  4071. SetupViewport(width, height);
  4072. CORE.Window.currentFbo.width = width;
  4073. CORE.Window.currentFbo.height = height;
  4074. CORE.Window.resizedLastFrame = true;
  4075. if (IsWindowFullscreen()) return;
  4076. // Set current screen size
  4077. CORE.Window.screen.width = width;
  4078. CORE.Window.screen.height = height;
  4079. // NOTE: Postprocessing texture is not scaled to new size
  4080. }
  4081. // GLFW3 WindowIconify Callback, runs when window is minimized/restored
  4082. static void WindowIconifyCallback(GLFWwindow *window, int iconified)
  4083. {
  4084. if (iconified) CORE.Window.flags |= FLAG_WINDOW_MINIMIZED; // The window was iconified
  4085. else CORE.Window.flags &= ~FLAG_WINDOW_MINIMIZED; // The window was restored
  4086. }
  4087. #if !defined(PLATFORM_WEB)
  4088. // GLFW3 WindowMaximize Callback, runs when window is maximized/restored
  4089. static void WindowMaximizeCallback(GLFWwindow *window, int maximized)
  4090. {
  4091. if (maximized) CORE.Window.flags |= FLAG_WINDOW_MAXIMIZED; // The window was maximized
  4092. else CORE.Window.flags &= ~FLAG_WINDOW_MAXIMIZED; // The window was restored
  4093. }
  4094. #endif
  4095. // GLFW3 WindowFocus Callback, runs when window get/lose focus
  4096. static void WindowFocusCallback(GLFWwindow *window, int focused)
  4097. {
  4098. if (focused) CORE.Window.flags &= ~FLAG_WINDOW_UNFOCUSED; // The window was focused
  4099. else CORE.Window.flags |= FLAG_WINDOW_UNFOCUSED; // The window lost focus
  4100. }
  4101. // GLFW3 Keyboard Callback, runs on key pressed
  4102. static void KeyCallback(GLFWwindow *window, int key, int scancode, int action, int mods)
  4103. {
  4104. //TRACELOG(LOG_DEBUG, "Key Callback: KEY:%i(%c) - SCANCODE:%i (STATE:%i)", key, key, scancode, action);
  4105. if (key == CORE.Input.Keyboard.exitKey && action == GLFW_PRESS)
  4106. {
  4107. glfwSetWindowShouldClose(CORE.Window.handle, GLFW_TRUE);
  4108. // NOTE: Before closing window, while loop must be left!
  4109. }
  4110. #if defined(SUPPORT_SCREEN_CAPTURE)
  4111. else if (key == GLFW_KEY_F12 && action == GLFW_PRESS)
  4112. {
  4113. #if defined(SUPPORT_GIF_RECORDING)
  4114. if (mods == GLFW_MOD_CONTROL)
  4115. {
  4116. if (gifRecording)
  4117. {
  4118. gifRecording = false;
  4119. MsfGifResult result = msf_gif_end(&gifState);
  4120. SaveFileData(TextFormat("%s/screenrec%03i.gif", CORE.Storage.basePath, screenshotCounter), result.data, (unsigned int)result.dataSize);
  4121. msf_gif_free(result);
  4122. #if defined(PLATFORM_WEB)
  4123. // Download file from MEMFS (emscripten memory filesystem)
  4124. // saveFileFromMEMFSToDisk() function is defined in raylib/templates/web_shel/shell.html
  4125. emscripten_run_script(TextFormat("saveFileFromMEMFSToDisk('%s','%s')", TextFormat("screenrec%03i.gif", screenshotCounter - 1), TextFormat("screenrec%03i.gif", screenshotCounter - 1)));
  4126. #endif
  4127. TRACELOG(LOG_INFO, "SYSTEM: Finish animated GIF recording");
  4128. }
  4129. else
  4130. {
  4131. gifRecording = true;
  4132. gifFrameCounter = 0;
  4133. msf_gif_begin(&gifState, CORE.Window.screen.width, CORE.Window.screen.height);
  4134. screenshotCounter++;
  4135. TRACELOG(LOG_INFO, "SYSTEM: Start animated GIF recording: %s", TextFormat("screenrec%03i.gif", screenshotCounter));
  4136. }
  4137. }
  4138. else
  4139. #endif // SUPPORT_GIF_RECORDING
  4140. {
  4141. TakeScreenshot(TextFormat("screenshot%03i.png", screenshotCounter));
  4142. screenshotCounter++;
  4143. }
  4144. }
  4145. #endif // SUPPORT_SCREEN_CAPTURE
  4146. #if defined(SUPPORT_EVENTS_AUTOMATION)
  4147. else if (key == GLFW_KEY_F11 && action == GLFW_PRESS)
  4148. {
  4149. eventsRecording = !eventsRecording;
  4150. // On finish recording, we export events into a file
  4151. if (!eventsRecording) ExportAutomationEvents("eventsrec.rep");
  4152. }
  4153. else if (key == GLFW_KEY_F9 && action == GLFW_PRESS)
  4154. {
  4155. LoadAutomationEvents("eventsrec.rep");
  4156. eventsPlaying = true;
  4157. TRACELOG(LOG_WARNING, "eventsPlaying enabled!");
  4158. }
  4159. #endif
  4160. else
  4161. {
  4162. // WARNING: GLFW could return GLFW_REPEAT, we need to consider it as 1
  4163. // to work properly with our implementation (IsKeyDown/IsKeyUp checks)
  4164. if (action == GLFW_RELEASE) CORE.Input.Keyboard.currentKeyState[key] = 0;
  4165. else CORE.Input.Keyboard.currentKeyState[key] = 1;
  4166. // Check if there is space available in the key queue
  4167. if ((CORE.Input.Keyboard.keyPressedQueueCount < MAX_KEY_PRESSED_QUEUE) && (action == GLFW_PRESS))
  4168. {
  4169. // Add character to the queue
  4170. CORE.Input.Keyboard.keyPressedQueue[CORE.Input.Keyboard.keyPressedQueueCount] = key;
  4171. CORE.Input.Keyboard.keyPressedQueueCount++;
  4172. }
  4173. }
  4174. }
  4175. // GLFW3 Char Key Callback, runs on key down (gets equivalent unicode char value)
  4176. static void CharCallback(GLFWwindow *window, unsigned int key)
  4177. {
  4178. //TRACELOG(LOG_DEBUG, "Char Callback: KEY:%i(%c)", key, key);
  4179. // NOTE: Registers any key down considering OS keyboard layout but
  4180. // do not detects action events, those should be managed by user...
  4181. // Ref: https://github.com/glfw/glfw/issues/668#issuecomment-166794907
  4182. // Ref: https://www.glfw.org/docs/latest/input_guide.html#input_char
  4183. // Check if there is space available in the queue
  4184. if (CORE.Input.Keyboard.charPressedQueueCount < MAX_KEY_PRESSED_QUEUE)
  4185. {
  4186. // Add character to the queue
  4187. CORE.Input.Keyboard.charPressedQueue[CORE.Input.Keyboard.charPressedQueueCount] = key;
  4188. CORE.Input.Keyboard.charPressedQueueCount++;
  4189. }
  4190. }
  4191. // GLFW3 Mouse Button Callback, runs on mouse button pressed
  4192. static void MouseButtonCallback(GLFWwindow *window, int button, int action, int mods)
  4193. {
  4194. // WARNING: GLFW could only return GLFW_PRESS (1) or GLFW_RELEASE (0) for now,
  4195. // but future releases may add more actions (i.e. GLFW_REPEAT)
  4196. CORE.Input.Mouse.currentButtonState[button] = action;
  4197. #if defined(SUPPORT_GESTURES_SYSTEM) && defined(SUPPORT_MOUSE_GESTURES) // PLATFORM_DESKTOP
  4198. // Process mouse events as touches to be able to use mouse-gestures
  4199. GestureEvent gestureEvent = { 0 };
  4200. // Register touch actions
  4201. if ((CORE.Input.Mouse.currentButtonState[button] == 1) && (CORE.Input.Mouse.previousButtonState[button] == 0)) gestureEvent.touchAction = TOUCH_ACTION_DOWN;
  4202. else if ((CORE.Input.Mouse.currentButtonState[button] == 0) && (CORE.Input.Mouse.previousButtonState[button] == 1)) gestureEvent.touchAction = TOUCH_ACTION_UP;
  4203. // NOTE: TOUCH_ACTION_MOVE event is registered in MouseCursorPosCallback()
  4204. // Assign a pointer ID
  4205. gestureEvent.pointId[0] = 0;
  4206. // Register touch points count
  4207. gestureEvent.pointCount = 1;
  4208. // Register touch points position, only one point registered
  4209. gestureEvent.position[0] = GetMousePosition();
  4210. // Normalize gestureEvent.position[0] for CORE.Window.screen.width and CORE.Window.screen.height
  4211. gestureEvent.position[0].x /= (float)GetScreenWidth();
  4212. gestureEvent.position[0].y /= (float)GetScreenHeight();
  4213. // Gesture data is sent to gestures system for processing
  4214. ProcessGestureEvent(gestureEvent);
  4215. #endif
  4216. }
  4217. // GLFW3 Cursor Position Callback, runs on mouse move
  4218. static void MouseCursorPosCallback(GLFWwindow *window, double x, double y)
  4219. {
  4220. CORE.Input.Mouse.currentPosition.x = (float)x;
  4221. CORE.Input.Mouse.currentPosition.y = (float)y;
  4222. CORE.Input.Touch.position[0] = CORE.Input.Mouse.currentPosition;
  4223. #if defined(SUPPORT_GESTURES_SYSTEM) && defined(SUPPORT_MOUSE_GESTURES) // PLATFORM_DESKTOP
  4224. // Process mouse events as touches to be able to use mouse-gestures
  4225. GestureEvent gestureEvent = { 0 };
  4226. gestureEvent.touchAction = TOUCH_ACTION_MOVE;
  4227. // Assign a pointer ID
  4228. gestureEvent.pointId[0] = 0;
  4229. // Register touch points count
  4230. gestureEvent.pointCount = 1;
  4231. // Register touch points position, only one point registered
  4232. gestureEvent.position[0] = CORE.Input.Touch.position[0];
  4233. // Normalize gestureEvent.position[0] for CORE.Window.screen.width and CORE.Window.screen.height
  4234. gestureEvent.position[0].x /= (float)GetScreenWidth();
  4235. gestureEvent.position[0].y /= (float)GetScreenHeight();
  4236. // Gesture data is sent to gestures system for processing
  4237. ProcessGestureEvent(gestureEvent);
  4238. #endif
  4239. }
  4240. // GLFW3 Srolling Callback, runs on mouse wheel
  4241. static void MouseScrollCallback(GLFWwindow *window, double xoffset, double yoffset)
  4242. {
  4243. if (xoffset != 0.0) CORE.Input.Mouse.currentWheelMove = (float)xoffset;
  4244. else CORE.Input.Mouse.currentWheelMove = (float)yoffset;
  4245. }
  4246. // GLFW3 CursorEnter Callback, when cursor enters the window
  4247. static void CursorEnterCallback(GLFWwindow *window, int enter)
  4248. {
  4249. if (enter == true) CORE.Input.Mouse.cursorOnScreen = true;
  4250. else CORE.Input.Mouse.cursorOnScreen = false;
  4251. }
  4252. // GLFW3 Window Drop Callback, runs when drop files into window
  4253. // NOTE: Paths are stored in dynamic memory for further retrieval
  4254. // Everytime new files are dropped, old ones are discarded
  4255. static void WindowDropCallback(GLFWwindow *window, int count, const char **paths)
  4256. {
  4257. ClearDroppedFiles();
  4258. CORE.Window.dropFilesPath = (char **)RL_MALLOC(count*sizeof(char *));
  4259. for (int i = 0; i < count; i++)
  4260. {
  4261. CORE.Window.dropFilesPath[i] = (char *)RL_MALLOC(MAX_FILEPATH_LENGTH*sizeof(char));
  4262. strcpy(CORE.Window.dropFilesPath[i], paths[i]);
  4263. }
  4264. CORE.Window.dropFileCount = count;
  4265. }
  4266. #endif
  4267. #if defined(PLATFORM_ANDROID)
  4268. // ANDROID: Process activity lifecycle commands
  4269. static void AndroidCommandCallback(struct android_app *app, int32_t cmd)
  4270. {
  4271. switch (cmd)
  4272. {
  4273. case APP_CMD_START:
  4274. {
  4275. //rendering = true;
  4276. } break;
  4277. case APP_CMD_RESUME: break;
  4278. case APP_CMD_INIT_WINDOW:
  4279. {
  4280. if (app->window != NULL)
  4281. {
  4282. if (CORE.Android.contextRebindRequired)
  4283. {
  4284. // Reset screen scaling to full display size
  4285. EGLint displayFormat = 0;
  4286. eglGetConfigAttrib(CORE.Window.device, CORE.Window.config, EGL_NATIVE_VISUAL_ID, &displayFormat);
  4287. ANativeWindow_setBuffersGeometry(app->window, CORE.Window.render.width, CORE.Window.render.height, displayFormat);
  4288. // Recreate display surface and re-attach OpenGL context
  4289. CORE.Window.surface = eglCreateWindowSurface(CORE.Window.device, CORE.Window.config, app->window, NULL);
  4290. eglMakeCurrent(CORE.Window.device, CORE.Window.surface, CORE.Window.surface, CORE.Window.context);
  4291. CORE.Android.contextRebindRequired = false;
  4292. }
  4293. else
  4294. {
  4295. CORE.Window.display.width = ANativeWindow_getWidth(CORE.Android.app->window);
  4296. CORE.Window.display.height = ANativeWindow_getHeight(CORE.Android.app->window);
  4297. // Initialize graphics device (display device and OpenGL context)
  4298. InitGraphicsDevice(CORE.Window.screen.width, CORE.Window.screen.height);
  4299. // Initialize hi-res timer
  4300. InitTimer();
  4301. // Initialize random seed
  4302. srand((unsigned int)time(NULL));
  4303. #if defined(SUPPORT_DEFAULT_FONT)
  4304. // Load default font
  4305. // NOTE: External function (defined in module: text)
  4306. LoadFontDefault();
  4307. Rectangle rec = GetFontDefault().recs[95];
  4308. // NOTE: We setup a 1px padding on char rectangle to avoid pixel bleeding on MSAA filtering
  4309. SetShapesTexture(GetFontDefault().texture, (Rectangle){ rec.x + 1, rec.y + 1, rec.width - 2, rec.height - 2 });
  4310. #endif
  4311. // TODO: GPU assets reload in case of lost focus (lost context)
  4312. // NOTE: This problem has been solved just unbinding and rebinding context from display
  4313. /*
  4314. if (assetsReloadRequired)
  4315. {
  4316. for (int i = 0; i < assetCount; i++)
  4317. {
  4318. // TODO: Unload old asset if required
  4319. // Load texture again to pointed texture
  4320. (*textureAsset + i) = LoadTexture(assetPath[i]);
  4321. }
  4322. }
  4323. */
  4324. }
  4325. }
  4326. } break;
  4327. case APP_CMD_GAINED_FOCUS:
  4328. {
  4329. CORE.Android.appEnabled = true;
  4330. //ResumeMusicStream();
  4331. } break;
  4332. case APP_CMD_PAUSE: break;
  4333. case APP_CMD_LOST_FOCUS:
  4334. {
  4335. CORE.Android.appEnabled = false;
  4336. //PauseMusicStream();
  4337. } break;
  4338. case APP_CMD_TERM_WINDOW:
  4339. {
  4340. // Dettach OpenGL context and destroy display surface
  4341. // NOTE 1: Detaching context before destroying display surface avoids losing our resources (textures, shaders, VBOs...)
  4342. // NOTE 2: In some cases (too many context loaded), OS could unload context automatically... :(
  4343. eglMakeCurrent(CORE.Window.device, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
  4344. eglDestroySurface(CORE.Window.device, CORE.Window.surface);
  4345. CORE.Android.contextRebindRequired = true;
  4346. } break;
  4347. case APP_CMD_SAVE_STATE: break;
  4348. case APP_CMD_STOP: break;
  4349. case APP_CMD_DESTROY:
  4350. {
  4351. // TODO: Finish activity?
  4352. //ANativeActivity_finish(CORE.Android.app->activity);
  4353. } break;
  4354. case APP_CMD_CONFIG_CHANGED:
  4355. {
  4356. //AConfiguration_fromAssetManager(CORE.Android.app->config, CORE.Android.app->activity->assetManager);
  4357. //print_cur_config(CORE.Android.app);
  4358. // Check screen orientation here!
  4359. } break;
  4360. default: break;
  4361. }
  4362. }
  4363. // ANDROID: Get input events
  4364. static int32_t AndroidInputCallback(struct android_app *app, AInputEvent *event)
  4365. {
  4366. // If additional inputs are required check:
  4367. // https://developer.android.com/ndk/reference/group/input
  4368. // https://developer.android.com/training/game-controllers/controller-input
  4369. int type = AInputEvent_getType(event);
  4370. int source = AInputEvent_getSource(event);
  4371. if (type == AINPUT_EVENT_TYPE_MOTION)
  4372. {
  4373. if (((source & AINPUT_SOURCE_JOYSTICK) == AINPUT_SOURCE_JOYSTICK) ||
  4374. ((source & AINPUT_SOURCE_GAMEPAD) == AINPUT_SOURCE_GAMEPAD))
  4375. {
  4376. // Get first touch position
  4377. CORE.Input.Touch.position[0].x = AMotionEvent_getX(event, 0);
  4378. CORE.Input.Touch.position[0].y = AMotionEvent_getY(event, 0);
  4379. // Get second touch position
  4380. CORE.Input.Touch.position[1].x = AMotionEvent_getX(event, 1);
  4381. CORE.Input.Touch.position[1].y = AMotionEvent_getY(event, 1);
  4382. int32_t keycode = AKeyEvent_getKeyCode(event);
  4383. if (AKeyEvent_getAction(event) == AKEY_EVENT_ACTION_DOWN)
  4384. {
  4385. CORE.Input.Keyboard.currentKeyState[keycode] = 1; // Key down
  4386. CORE.Input.Keyboard.keyPressedQueue[CORE.Input.Keyboard.keyPressedQueueCount] = keycode;
  4387. CORE.Input.Keyboard.keyPressedQueueCount++;
  4388. }
  4389. else CORE.Input.Keyboard.currentKeyState[keycode] = 0; // Key up
  4390. // Stop processing gamepad buttons
  4391. return 1;
  4392. }
  4393. }
  4394. else if (type == AINPUT_EVENT_TYPE_KEY)
  4395. {
  4396. int32_t keycode = AKeyEvent_getKeyCode(event);
  4397. //int32_t AKeyEvent_getMetaState(event);
  4398. // Save current button and its state
  4399. // NOTE: Android key action is 0 for down and 1 for up
  4400. if (AKeyEvent_getAction(event) == AKEY_EVENT_ACTION_DOWN)
  4401. {
  4402. CORE.Input.Keyboard.currentKeyState[keycode] = 1; // Key down
  4403. CORE.Input.Keyboard.keyPressedQueue[CORE.Input.Keyboard.keyPressedQueueCount] = keycode;
  4404. CORE.Input.Keyboard.keyPressedQueueCount++;
  4405. }
  4406. else CORE.Input.Keyboard.currentKeyState[keycode] = 0; // Key up
  4407. if (keycode == AKEYCODE_POWER)
  4408. {
  4409. // Let the OS handle input to avoid app stuck. Behaviour: CMD_PAUSE -> CMD_SAVE_STATE -> CMD_STOP -> CMD_CONFIG_CHANGED -> CMD_LOST_FOCUS
  4410. // Resuming Behaviour: CMD_START -> CMD_RESUME -> CMD_CONFIG_CHANGED -> CMD_CONFIG_CHANGED -> CMD_GAINED_FOCUS
  4411. // It seems like locking mobile, screen size (CMD_CONFIG_CHANGED) is affected.
  4412. // NOTE: AndroidManifest.xml must have <activity android:configChanges="orientation|keyboardHidden|screenSize" >
  4413. // Before that change, activity was calling CMD_TERM_WINDOW and CMD_DESTROY when locking mobile, so that was not a normal behaviour
  4414. return 0;
  4415. }
  4416. else if ((keycode == AKEYCODE_BACK) || (keycode == AKEYCODE_MENU))
  4417. {
  4418. // Eat BACK_BUTTON and AKEYCODE_MENU, just do nothing... and don't let to be handled by OS!
  4419. return 1;
  4420. }
  4421. else if ((keycode == AKEYCODE_VOLUME_UP) || (keycode == AKEYCODE_VOLUME_DOWN))
  4422. {
  4423. // Set default OS behaviour
  4424. return 0;
  4425. }
  4426. return 0;
  4427. }
  4428. // Register touch points count
  4429. CORE.Input.Touch.pointCount = AMotionEvent_getPointerCount(event);
  4430. for (int i = 0; (i < CORE.Input.Touch.pointCount) && (i < MAX_TOUCH_POINTS); i++)
  4431. {
  4432. // Register touch points id
  4433. CORE.Input.Touch.pointId[i] = AMotionEvent_getPointerId(event, i);
  4434. // Register touch points position
  4435. CORE.Input.Touch.position[i] = (Vector2){ AMotionEvent_getX(event, i), AMotionEvent_getY(event, i) };
  4436. // Normalize CORE.Input.Touch.position[x] for screenWidth and screenHeight
  4437. CORE.Input.Touch.position[i].x /= (float)GetScreenWidth();
  4438. CORE.Input.Touch.position[i].y /= (float)GetScreenHeight();
  4439. }
  4440. int32_t action = AMotionEvent_getAction(event);
  4441. unsigned int flags = action & AMOTION_EVENT_ACTION_MASK;
  4442. if (flags == AMOTION_EVENT_ACTION_DOWN || flags == AMOTION_EVENT_ACTION_MOVE) CORE.Input.Touch.currentTouchState[MOUSE_BUTTON_LEFT] = 1;
  4443. else if (flags == AMOTION_EVENT_ACTION_UP) CORE.Input.Touch.currentTouchState[MOUSE_BUTTON_LEFT] = 0;
  4444. #if defined(SUPPORT_GESTURES_SYSTEM) // PLATFORM_ANDROID
  4445. GestureEvent gestureEvent = { 0 };
  4446. gestureEvent.pointCount = CORE.Input.Touch.pointCount;
  4447. // Register touch actions
  4448. if (flags == AMOTION_EVENT_ACTION_DOWN) gestureEvent.touchAction = TOUCH_ACTION_DOWN;
  4449. else if (flags == AMOTION_EVENT_ACTION_UP) gestureEvent.touchAction = TOUCH_ACTION_UP;
  4450. else if (flags == AMOTION_EVENT_ACTION_MOVE) gestureEvent.touchAction = TOUCH_ACTION_MOVE;
  4451. else if (flags == AMOTION_EVENT_ACTION_CANCEL) gestureEvent.touchAction = TOUCH_ACTION_CANCEL;
  4452. for (int i = 0; (i < gestureEvent.pointCount) && (i < MAX_TOUCH_POINTS); i++)
  4453. {
  4454. gestureEvent.pointId[i] = CORE.Input.Touch.pointId[i];
  4455. gestureEvent.position[i] = CORE.Input.Touch.position[i];
  4456. }
  4457. // Gesture data is sent to gestures system for processing
  4458. ProcessGestureEvent(gestureEvent);
  4459. #endif
  4460. return 0;
  4461. }
  4462. #endif
  4463. #if defined(PLATFORM_WEB)
  4464. // Register mouse input events
  4465. static EM_BOOL EmscriptenMouseCallback(int eventType, const EmscriptenMouseEvent *mouseEvent, void *userData)
  4466. {
  4467. // This is only for registering mouse click events with emscripten and doesn't need to do anything
  4468. return 0;
  4469. }
  4470. // Register touch input events
  4471. static EM_BOOL EmscriptenTouchCallback(int eventType, const EmscriptenTouchEvent *touchEvent, void *userData)
  4472. {
  4473. // Register touch points count
  4474. CORE.Input.Touch.pointCount = touchEvent->numTouches;
  4475. double canvasWidth = 0.0;
  4476. double canvasHeight = 0.0;
  4477. // NOTE: emscripten_get_canvas_element_size() returns canvas.width and canvas.height but
  4478. // we are looking for actual CSS size: canvas.style.width and canvas.style.height
  4479. //EMSCRIPTEN_RESULT res = emscripten_get_canvas_element_size("#canvas", &canvasWidth, &canvasHeight);
  4480. emscripten_get_element_css_size("#canvas", &canvasWidth, &canvasHeight);
  4481. for (int i = 0; (i < CORE.Input.Touch.pointCount) && (i < MAX_TOUCH_POINTS); i++)
  4482. {
  4483. // Register touch points id
  4484. CORE.Input.Touch.pointId[i] = touchEvent->touches[i].identifier;
  4485. // Register touch points position
  4486. CORE.Input.Touch.position[i] = (Vector2){ touchEvent->touches[i].targetX, touchEvent->touches[i].targetY };
  4487. // Normalize gestureEvent.position[x] for CORE.Window.screen.width and CORE.Window.screen.height
  4488. CORE.Input.Touch.position[i].x *= ((float)GetScreenWidth()/(float)canvasWidth);
  4489. CORE.Input.Touch.position[i].y *= ((float)GetScreenHeight()/(float)canvasHeight);
  4490. if (eventType == EMSCRIPTEN_EVENT_TOUCHSTART) CORE.Input.Touch.currentTouchState[i] = 1;
  4491. else if (eventType == EMSCRIPTEN_EVENT_TOUCHEND) CORE.Input.Touch.currentTouchState[i] = 0;
  4492. }
  4493. #if defined(SUPPORT_GESTURES_SYSTEM) // PLATFORM_WEB
  4494. GestureEvent gestureEvent = { 0 };
  4495. gestureEvent.pointCount = CORE.Input.Touch.pointCount;
  4496. // Register touch actions
  4497. if (eventType == EMSCRIPTEN_EVENT_TOUCHSTART) gestureEvent.touchAction = TOUCH_ACTION_DOWN;
  4498. else if (eventType == EMSCRIPTEN_EVENT_TOUCHEND) gestureEvent.touchAction = TOUCH_ACTION_UP;
  4499. else if (eventType == EMSCRIPTEN_EVENT_TOUCHMOVE) gestureEvent.touchAction = TOUCH_ACTION_MOVE;
  4500. else if (eventType == EMSCRIPTEN_EVENT_TOUCHCANCEL) gestureEvent.touchAction = TOUCH_ACTION_CANCEL;
  4501. for (int i = 0; (i < gestureEvent.pointCount) && (i < MAX_TOUCH_POINTS); i++)
  4502. {
  4503. gestureEvent.pointId[i] = CORE.Input.Touch.pointId[i];
  4504. gestureEvent.position[i] = CORE.Input.Touch.position[i];
  4505. }
  4506. // Gesture data is sent to gestures system for processing
  4507. ProcessGestureEvent(gestureEvent);
  4508. #endif
  4509. return 1;
  4510. }
  4511. // Register connected/disconnected gamepads events
  4512. static EM_BOOL EmscriptenGamepadCallback(int eventType, const EmscriptenGamepadEvent *gamepadEvent, void *userData)
  4513. {
  4514. /*
  4515. TRACELOGD("%s: timeStamp: %g, connected: %d, index: %ld, numAxes: %d, numButtons: %d, id: \"%s\", mapping: \"%s\"",
  4516. eventType != 0? emscripten_event_type_to_string(eventType) : "Gamepad state",
  4517. gamepadEvent->timestamp, gamepadEvent->connected, gamepadEvent->index, gamepadEvent->numAxes, gamepadEvent->numButtons, gamepadEvent->id, gamepadEvent->mapping);
  4518. for (int i = 0; i < gamepadEvent->numAxes; ++i) TRACELOGD("Axis %d: %g", i, gamepadEvent->axis[i]);
  4519. for (int i = 0; i < gamepadEvent->numButtons; ++i) TRACELOGD("Button %d: Digital: %d, Analog: %g", i, gamepadEvent->digitalButton[i], gamepadEvent->analogButton[i]);
  4520. */
  4521. if ((gamepadEvent->connected) && (gamepadEvent->index < MAX_GAMEPADS))
  4522. {
  4523. CORE.Input.Gamepad.ready[gamepadEvent->index] = true;
  4524. sprintf(CORE.Input.Gamepad.name[gamepadEvent->index],"%s",gamepadEvent->id);
  4525. }
  4526. else CORE.Input.Gamepad.ready[gamepadEvent->index] = false;
  4527. // TODO: Test gamepadEvent->index
  4528. return 0;
  4529. }
  4530. #endif
  4531. #if defined(PLATFORM_RPI) || defined(PLATFORM_DRM)
  4532. // Initialize Keyboard system (using standard input)
  4533. static void InitKeyboard(void)
  4534. {
  4535. // NOTE: We read directly from Standard Input (stdin) - STDIN_FILENO file descriptor,
  4536. // Reading directly from stdin will give chars already key-mapped by kernel to ASCII or UNICODE
  4537. // Save terminal keyboard settings
  4538. tcgetattr(STDIN_FILENO, &CORE.Input.Keyboard.defaultSettings);
  4539. // Reconfigure terminal with new settings
  4540. struct termios keyboardNewSettings = { 0 };
  4541. keyboardNewSettings = CORE.Input.Keyboard.defaultSettings;
  4542. // New terminal settings for keyboard: turn off buffering (non-canonical mode), echo and key processing
  4543. // NOTE: ISIG controls if ^C and ^Z generate break signals or not
  4544. keyboardNewSettings.c_lflag &= ~(ICANON | ECHO | ISIG);
  4545. //keyboardNewSettings.c_iflag &= ~(ISTRIP | INLCR | ICRNL | IGNCR | IXON | IXOFF);
  4546. keyboardNewSettings.c_cc[VMIN] = 1;
  4547. keyboardNewSettings.c_cc[VTIME] = 0;
  4548. // Set new keyboard settings (change occurs immediately)
  4549. tcsetattr(STDIN_FILENO, TCSANOW, &keyboardNewSettings);
  4550. // Save old keyboard mode to restore it at the end
  4551. CORE.Input.Keyboard.defaultFileFlags = fcntl(STDIN_FILENO, F_GETFL, 0); // F_GETFL: Get the file access mode and the file status flags
  4552. fcntl(STDIN_FILENO, F_SETFL, CORE.Input.Keyboard.defaultFileFlags | O_NONBLOCK); // F_SETFL: Set the file status flags to the value specified
  4553. // NOTE: If ioctl() returns -1, it means the call failed for some reason (error code set in errno)
  4554. int result = ioctl(STDIN_FILENO, KDGKBMODE, &CORE.Input.Keyboard.defaultMode);
  4555. // In case of failure, it could mean a remote keyboard is used (SSH)
  4556. if (result < 0) TRACELOG(LOG_WARNING, "RPI: Failed to change keyboard mode, an SSH keyboard is probably used");
  4557. else
  4558. {
  4559. // Reconfigure keyboard mode to get:
  4560. // - scancodes (K_RAW)
  4561. // - keycodes (K_MEDIUMRAW)
  4562. // - ASCII chars (K_XLATE)
  4563. // - UNICODE chars (K_UNICODE)
  4564. ioctl(STDIN_FILENO, KDSKBMODE, K_XLATE); // ASCII chars
  4565. }
  4566. // Register keyboard restore when program finishes
  4567. atexit(RestoreKeyboard);
  4568. }
  4569. // Restore default keyboard input
  4570. static void RestoreKeyboard(void)
  4571. {
  4572. // Reset to default keyboard settings
  4573. tcsetattr(STDIN_FILENO, TCSANOW, &CORE.Input.Keyboard.defaultSettings);
  4574. // Reconfigure keyboard to default mode
  4575. fcntl(STDIN_FILENO, F_SETFL, CORE.Input.Keyboard.defaultFileFlags);
  4576. ioctl(STDIN_FILENO, KDSKBMODE, CORE.Input.Keyboard.defaultMode);
  4577. }
  4578. #if defined(SUPPORT_SSH_KEYBOARD_RPI)
  4579. // Process keyboard inputs
  4580. // TODO: Most probably input reading and processing should be in a separate thread
  4581. static void ProcessKeyboard(void)
  4582. {
  4583. #define MAX_KEYBUFFER_SIZE 32 // Max size in bytes to read
  4584. // Keyboard input polling (fill keys[256] array with status)
  4585. int bufferByteCount = 0; // Bytes available on the buffer
  4586. char keysBuffer[MAX_KEYBUFFER_SIZE]; // Max keys to be read at a time
  4587. // Read availables keycodes from stdin
  4588. bufferByteCount = read(STDIN_FILENO, keysBuffer, MAX_KEYBUFFER_SIZE); // POSIX system call
  4589. // Reset pressed keys array (it will be filled below)
  4590. for (int i = 0; i < MAX_KEYBOARD_KEYS; i++) CORE.Input.Keyboard.currentKeyState[i] = 0;
  4591. // Fill all read bytes (looking for keys)
  4592. for (int i = 0; i < bufferByteCount; i++)
  4593. {
  4594. // NOTE: If (key == 0x1b), depending on next key, it could be a special keymap code!
  4595. // Up -> 1b 5b 41 / Left -> 1b 5b 44 / Right -> 1b 5b 43 / Down -> 1b 5b 42
  4596. if (keysBuffer[i] == 0x1b)
  4597. {
  4598. // Check if ESCAPE key has been pressed to stop program
  4599. if (bufferByteCount == 1) CORE.Input.Keyboard.currentKeyState[CORE.Input.Keyboard.exitKey] = 1;
  4600. else
  4601. {
  4602. if (keysBuffer[i + 1] == 0x5b) // Special function key
  4603. {
  4604. if ((keysBuffer[i + 2] == 0x5b) || (keysBuffer[i + 2] == 0x31) || (keysBuffer[i + 2] == 0x32))
  4605. {
  4606. // Process special function keys (F1 - F12)
  4607. switch (keysBuffer[i + 3])
  4608. {
  4609. case 0x41: CORE.Input.Keyboard.currentKeyState[290] = 1; break; // raylib KEY_F1
  4610. case 0x42: CORE.Input.Keyboard.currentKeyState[291] = 1; break; // raylib KEY_F2
  4611. case 0x43: CORE.Input.Keyboard.currentKeyState[292] = 1; break; // raylib KEY_F3
  4612. case 0x44: CORE.Input.Keyboard.currentKeyState[293] = 1; break; // raylib KEY_F4
  4613. case 0x45: CORE.Input.Keyboard.currentKeyState[294] = 1; break; // raylib KEY_F5
  4614. case 0x37: CORE.Input.Keyboard.currentKeyState[295] = 1; break; // raylib KEY_F6
  4615. case 0x38: CORE.Input.Keyboard.currentKeyState[296] = 1; break; // raylib KEY_F7
  4616. case 0x39: CORE.Input.Keyboard.currentKeyState[297] = 1; break; // raylib KEY_F8
  4617. case 0x30: CORE.Input.Keyboard.currentKeyState[298] = 1; break; // raylib KEY_F9
  4618. case 0x31: CORE.Input.Keyboard.currentKeyState[299] = 1; break; // raylib KEY_F10
  4619. case 0x33: CORE.Input.Keyboard.currentKeyState[300] = 1; break; // raylib KEY_F11
  4620. case 0x34: CORE.Input.Keyboard.currentKeyState[301] = 1; break; // raylib KEY_F12
  4621. default: break;
  4622. }
  4623. if (keysBuffer[i + 2] == 0x5b) i += 4;
  4624. else if ((keysBuffer[i + 2] == 0x31) || (keysBuffer[i + 2] == 0x32)) i += 5;
  4625. }
  4626. else
  4627. {
  4628. switch (keysBuffer[i + 2])
  4629. {
  4630. case 0x41: CORE.Input.Keyboard.currentKeyState[265] = 1; break; // raylib KEY_UP
  4631. case 0x42: CORE.Input.Keyboard.currentKeyState[264] = 1; break; // raylib KEY_DOWN
  4632. case 0x43: CORE.Input.Keyboard.currentKeyState[262] = 1; break; // raylib KEY_RIGHT
  4633. case 0x44: CORE.Input.Keyboard.currentKeyState[263] = 1; break; // raylib KEY_LEFT
  4634. default: break;
  4635. }
  4636. i += 3; // Jump to next key
  4637. }
  4638. // NOTE: Some keys are not directly keymapped (CTRL, ALT, SHIFT)
  4639. }
  4640. }
  4641. }
  4642. else if (keysBuffer[i] == 0x0a) // raylib KEY_ENTER (don't mix with <linux/input.h> KEY_*)
  4643. {
  4644. CORE.Input.Keyboard.currentKeyState[257] = 1;
  4645. CORE.Input.Keyboard.keyPressedQueue[CORE.Input.Keyboard.keyPressedQueueCount] = 257; // Add keys pressed into queue
  4646. CORE.Input.Keyboard.keyPressedQueueCount++;
  4647. }
  4648. else if (keysBuffer[i] == 0x7f) // raylib KEY_BACKSPACE
  4649. {
  4650. CORE.Input.Keyboard.currentKeyState[259] = 1;
  4651. CORE.Input.Keyboard.keyPressedQueue[CORE.Input.Keyboard.keyPressedQueueCount] = 257; // Add keys pressed into queue
  4652. CORE.Input.Keyboard.keyPressedQueueCount++;
  4653. }
  4654. else
  4655. {
  4656. // Translate lowercase a-z letters to A-Z
  4657. if ((keysBuffer[i] >= 97) && (keysBuffer[i] <= 122))
  4658. {
  4659. CORE.Input.Keyboard.currentKeyState[(int)keysBuffer[i] - 32] = 1;
  4660. }
  4661. else CORE.Input.Keyboard.currentKeyState[(int)keysBuffer[i]] = 1;
  4662. CORE.Input.Keyboard.keyPressedQueue[CORE.Input.Keyboard.keyPressedQueueCount] = keysBuffer[i]; // Add keys pressed into queue
  4663. CORE.Input.Keyboard.keyPressedQueueCount++;
  4664. }
  4665. }
  4666. // Check exit key (same functionality as GLFW3 KeyCallback())
  4667. if (CORE.Input.Keyboard.currentKeyState[CORE.Input.Keyboard.exitKey] == 1) CORE.Window.shouldClose = true;
  4668. #if defined(SUPPORT_SCREEN_CAPTURE)
  4669. // Check screen capture key (raylib key: KEY_F12)
  4670. if (CORE.Input.Keyboard.currentKeyState[301] == 1)
  4671. {
  4672. TakeScreenshot(TextFormat("screenshot%03i.png", screenshotCounter));
  4673. screenshotCounter++;
  4674. }
  4675. #endif
  4676. }
  4677. #endif // SUPPORT_SSH_KEYBOARD_RPI
  4678. // Initialise user input from evdev(/dev/input/event<N>) this means mouse, keyboard or gamepad devices
  4679. static void InitEvdevInput(void)
  4680. {
  4681. char path[MAX_FILEPATH_LENGTH] = { 0 };
  4682. DIR *directory = NULL;
  4683. struct dirent *entity = NULL;
  4684. // Initialise keyboard file descriptor
  4685. CORE.Input.Keyboard.fd = -1;
  4686. // Reset variables
  4687. for (int i = 0; i < MAX_TOUCH_POINTS; ++i)
  4688. {
  4689. CORE.Input.Touch.position[i].x = -1;
  4690. CORE.Input.Touch.position[i].y = -1;
  4691. }
  4692. // Reset keyboard key state
  4693. for (int i = 0; i < MAX_KEYBOARD_KEYS; i++) CORE.Input.Keyboard.currentKeyState[i] = 0;
  4694. // Open the linux directory of "/dev/input"
  4695. directory = opendir(DEFAULT_EVDEV_PATH);
  4696. if (directory)
  4697. {
  4698. while ((entity = readdir(directory)) != NULL)
  4699. {
  4700. if (strncmp("event", entity->d_name, strlen("event")) == 0) // Search for devices named "event*"
  4701. {
  4702. sprintf(path, "%s%s", DEFAULT_EVDEV_PATH, entity->d_name);
  4703. ConfigureEvdevDevice(path); // Configure the device if appropriate
  4704. }
  4705. }
  4706. closedir(directory);
  4707. }
  4708. else TRACELOG(LOG_WARNING, "RPI: Failed to open linux event directory: %s", DEFAULT_EVDEV_PATH);
  4709. }
  4710. // Identifies a input device and configures it for use if appropriate
  4711. static void ConfigureEvdevDevice(char *device)
  4712. {
  4713. #define BITS_PER_LONG (8*sizeof(long))
  4714. #define NBITS(x) ((((x) - 1)/BITS_PER_LONG) + 1)
  4715. #define OFF(x) ((x)%BITS_PER_LONG)
  4716. #define BIT(x) (1UL<<OFF(x))
  4717. #define LONG(x) ((x)/BITS_PER_LONG)
  4718. #define TEST_BIT(array, bit) ((array[LONG(bit)] >> OFF(bit)) & 1)
  4719. struct input_absinfo absinfo = { 0 };
  4720. unsigned long evBits[NBITS(EV_MAX)] = { 0 };
  4721. unsigned long absBits[NBITS(ABS_MAX)] = { 0 };
  4722. unsigned long relBits[NBITS(REL_MAX)] = { 0 };
  4723. unsigned long keyBits[NBITS(KEY_MAX)] = { 0 };
  4724. bool hasAbs = false;
  4725. bool hasRel = false;
  4726. bool hasAbsMulti = false;
  4727. int freeWorkerId = -1;
  4728. int fd = -1;
  4729. InputEventWorker *worker = NULL;
  4730. // Open the device and allocate worker
  4731. //-------------------------------------------------------------------------------------------------------
  4732. // Find a free spot in the workers array
  4733. for (int i = 0; i < sizeof(CORE.Input.eventWorker)/sizeof(InputEventWorker); ++i)
  4734. {
  4735. if (CORE.Input.eventWorker[i].threadId == 0)
  4736. {
  4737. freeWorkerId = i;
  4738. break;
  4739. }
  4740. }
  4741. // Select the free worker from array
  4742. if (freeWorkerId >= 0)
  4743. {
  4744. worker = &(CORE.Input.eventWorker[freeWorkerId]); // Grab a pointer to the worker
  4745. memset(worker, 0, sizeof(InputEventWorker)); // Clear the worker
  4746. }
  4747. else
  4748. {
  4749. TRACELOG(LOG_WARNING, "RPI: Failed to create input device thread for %s, out of worker slots", device);
  4750. return;
  4751. }
  4752. // Open the device
  4753. fd = open(device, O_RDONLY | O_NONBLOCK);
  4754. if (fd < 0)
  4755. {
  4756. TRACELOG(LOG_WARNING, "RPI: Failed to open input device %s", device);
  4757. return;
  4758. }
  4759. worker->fd = fd;
  4760. // Grab number on the end of the devices name "event<N>"
  4761. int devNum = 0;
  4762. char *ptrDevName = strrchr(device, 't');
  4763. worker->eventNum = -1;
  4764. if (ptrDevName != NULL)
  4765. {
  4766. if (sscanf(ptrDevName, "t%d", &devNum) == 1) worker->eventNum = devNum;
  4767. }
  4768. // At this point we have a connection to the device, but we don't yet know what the device is.
  4769. // It could be many things, even as simple as a power button...
  4770. //-------------------------------------------------------------------------------------------------------
  4771. // Identify the device
  4772. //-------------------------------------------------------------------------------------------------------
  4773. ioctl(fd, EVIOCGBIT(0, sizeof(evBits)), evBits); // Read a bitfield of the available device properties
  4774. // Check for absolute input devices
  4775. if (TEST_BIT(evBits, EV_ABS))
  4776. {
  4777. ioctl(fd, EVIOCGBIT(EV_ABS, sizeof(absBits)), absBits);
  4778. // Check for absolute movement support (usualy touchscreens, but also joysticks)
  4779. if (TEST_BIT(absBits, ABS_X) && TEST_BIT(absBits, ABS_Y))
  4780. {
  4781. hasAbs = true;
  4782. // Get the scaling values
  4783. ioctl(fd, EVIOCGABS(ABS_X), &absinfo);
  4784. worker->absRange.x = absinfo.minimum;
  4785. worker->absRange.width = absinfo.maximum - absinfo.minimum;
  4786. ioctl(fd, EVIOCGABS(ABS_Y), &absinfo);
  4787. worker->absRange.y = absinfo.minimum;
  4788. worker->absRange.height = absinfo.maximum - absinfo.minimum;
  4789. }
  4790. // Check for multiple absolute movement support (usualy multitouch touchscreens)
  4791. if (TEST_BIT(absBits, ABS_MT_POSITION_X) && TEST_BIT(absBits, ABS_MT_POSITION_Y))
  4792. {
  4793. hasAbsMulti = true;
  4794. // Get the scaling values
  4795. ioctl(fd, EVIOCGABS(ABS_X), &absinfo);
  4796. worker->absRange.x = absinfo.minimum;
  4797. worker->absRange.width = absinfo.maximum - absinfo.minimum;
  4798. ioctl(fd, EVIOCGABS(ABS_Y), &absinfo);
  4799. worker->absRange.y = absinfo.minimum;
  4800. worker->absRange.height = absinfo.maximum - absinfo.minimum;
  4801. }
  4802. }
  4803. // Check for relative movement support (usualy mouse)
  4804. if (TEST_BIT(evBits, EV_REL))
  4805. {
  4806. ioctl(fd, EVIOCGBIT(EV_REL, sizeof(relBits)), relBits);
  4807. if (TEST_BIT(relBits, REL_X) && TEST_BIT(relBits, REL_Y)) hasRel = true;
  4808. }
  4809. // Check for button support to determine the device type(usualy on all input devices)
  4810. if (TEST_BIT(evBits, EV_KEY))
  4811. {
  4812. ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(keyBits)), keyBits);
  4813. if (hasAbs || hasAbsMulti)
  4814. {
  4815. if (TEST_BIT(keyBits, BTN_TOUCH)) worker->isTouch = true; // This is a touchscreen
  4816. if (TEST_BIT(keyBits, BTN_TOOL_FINGER)) worker->isTouch = true; // This is a drawing tablet
  4817. if (TEST_BIT(keyBits, BTN_TOOL_PEN)) worker->isTouch = true; // This is a drawing tablet
  4818. if (TEST_BIT(keyBits, BTN_STYLUS)) worker->isTouch = true; // This is a drawing tablet
  4819. if (worker->isTouch || hasAbsMulti) worker->isMultitouch = true; // This is a multitouch capable device
  4820. }
  4821. if (hasRel)
  4822. {
  4823. if (TEST_BIT(keyBits, BTN_LEFT)) worker->isMouse = true; // This is a mouse
  4824. if (TEST_BIT(keyBits, BTN_RIGHT)) worker->isMouse = true; // This is a mouse
  4825. }
  4826. if (TEST_BIT(keyBits, BTN_A)) worker->isGamepad = true; // This is a gamepad
  4827. if (TEST_BIT(keyBits, BTN_TRIGGER)) worker->isGamepad = true; // This is a gamepad
  4828. if (TEST_BIT(keyBits, BTN_START)) worker->isGamepad = true; // This is a gamepad
  4829. if (TEST_BIT(keyBits, BTN_TL)) worker->isGamepad = true; // This is a gamepad
  4830. if (TEST_BIT(keyBits, BTN_TL)) worker->isGamepad = true; // This is a gamepad
  4831. if (TEST_BIT(keyBits, KEY_SPACE)) worker->isKeyboard = true; // This is a keyboard
  4832. }
  4833. //-------------------------------------------------------------------------------------------------------
  4834. // Decide what to do with the device
  4835. //-------------------------------------------------------------------------------------------------------
  4836. if (worker->isKeyboard && CORE.Input.Keyboard.fd == -1)
  4837. {
  4838. // Use the first keyboard encountered. This assumes that a device that says it's a keyboard is just a
  4839. // keyboard. The keyboard is polled synchronously, whereas other input devices are polled in separate
  4840. // threads so that they don't drop events when the frame rate is slow.
  4841. TRACELOG(LOG_INFO, "RPI: Opening keyboard device: %s", device);
  4842. CORE.Input.Keyboard.fd = worker->fd;
  4843. }
  4844. else if (worker->isTouch || worker->isMouse)
  4845. {
  4846. // Looks like an interesting device
  4847. TRACELOG(LOG_INFO, "RPI: Opening input device: %s (%s%s%s%s)", device,
  4848. worker->isMouse? "mouse " : "",
  4849. worker->isMultitouch? "multitouch " : "",
  4850. worker->isTouch? "touchscreen " : "",
  4851. worker->isGamepad? "gamepad " : "");
  4852. // Create a thread for this device
  4853. int error = pthread_create(&worker->threadId, NULL, &EventThread, (void *)worker);
  4854. if (error != 0)
  4855. {
  4856. TRACELOG(LOG_WARNING, "RPI: Failed to create input device thread: %s (error: %d)", device, error);
  4857. worker->threadId = 0;
  4858. close(fd);
  4859. }
  4860. #if defined(USE_LAST_TOUCH_DEVICE)
  4861. // Find touchscreen with the highest index
  4862. int maxTouchNumber = -1;
  4863. for (int i = 0; i < sizeof(CORE.Input.eventWorker)/sizeof(InputEventWorker); ++i)
  4864. {
  4865. if (CORE.Input.eventWorker[i].isTouch && (CORE.Input.eventWorker[i].eventNum > maxTouchNumber)) maxTouchNumber = CORE.Input.eventWorker[i].eventNum;
  4866. }
  4867. // Find touchscreens with lower indexes
  4868. for (int i = 0; i < sizeof(CORE.Input.eventWorker)/sizeof(InputEventWorker); ++i)
  4869. {
  4870. if (CORE.Input.eventWorker[i].isTouch && (CORE.Input.eventWorker[i].eventNum < maxTouchNumber))
  4871. {
  4872. if (CORE.Input.eventWorker[i].threadId != 0)
  4873. {
  4874. TRACELOG(LOG_WARNING, "RPI: Found duplicate touchscreen, killing touchscreen on event: %d", i);
  4875. pthread_cancel(CORE.Input.eventWorker[i].threadId);
  4876. close(CORE.Input.eventWorker[i].fd);
  4877. }
  4878. }
  4879. }
  4880. #endif
  4881. }
  4882. else close(fd); // We are not interested in this device
  4883. //-------------------------------------------------------------------------------------------------------
  4884. }
  4885. static void PollKeyboardEvents(void)
  4886. {
  4887. // Scancode to keycode mapping for US keyboards
  4888. // TODO: Probably replace this with a keymap from the X11 to get the correct regional map for the keyboard:
  4889. // Currently non US keyboards will have the wrong mapping for some keys
  4890. static const int keymapUS[] = {
  4891. 0, 256, 49, 50, 51, 52, 53, 54, 55, 56, 57, 48, 45, 61, 259, 258, 81, 87, 69, 82, 84,
  4892. 89, 85, 73, 79, 80, 91, 93, 257, 341, 65, 83, 68, 70, 71, 72, 74, 75, 76, 59, 39, 96,
  4893. 340, 92, 90, 88, 67, 86, 66, 78, 77, 44, 46, 47, 344, 332, 342, 32, 280, 290, 291,
  4894. 292, 293, 294, 295, 296, 297, 298, 299, 282, 281, 327, 328, 329, 333, 324, 325,
  4895. 326, 334, 321, 322, 323, 320, 330, 0, 85, 86, 300, 301, 89, 90, 91, 92, 93, 94, 95,
  4896. 335, 345, 331, 283, 346, 101, 268, 265, 266, 263, 262, 269, 264, 267, 260, 261,
  4897. 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 347, 127,
  4898. 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143,
  4899. 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159,
  4900. 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175,
  4901. 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191,
  4902. 192, 193, 194, 0, 0, 0, 0, 0, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210,
  4903. 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226,
  4904. 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242,
  4905. 243, 244, 245, 246, 247, 248, 0, 0, 0, 0, 0, 0, 0
  4906. };
  4907. int fd = CORE.Input.Keyboard.fd;
  4908. if (fd == -1) return;
  4909. struct input_event event = { 0 };
  4910. int keycode = -1;
  4911. // Try to read data from the keyboard and only continue if successful
  4912. while (read(fd, &event, sizeof(event)) == (int)sizeof(event))
  4913. {
  4914. // Button parsing
  4915. if (event.type == EV_KEY)
  4916. {
  4917. #if defined(SUPPORT_SSH_KEYBOARD_RPI)
  4918. // Change keyboard mode to events
  4919. CORE.Input.Keyboard.evtMode = true;
  4920. #endif
  4921. // Keyboard button parsing
  4922. if ((event.code >= 1) && (event.code <= 255)) //Keyboard keys appear for codes 1 to 255
  4923. {
  4924. keycode = keymapUS[event.code & 0xFF]; // The code we get is a scancode so we look up the apropriate keycode
  4925. // Make sure we got a valid keycode
  4926. if ((keycode > 0) && (keycode < sizeof(CORE.Input.Keyboard.currentKeyState)))
  4927. {
  4928. // WARNING: https://www.kernel.org/doc/Documentation/input/input.txt
  4929. // Event interface: 'value' is the value the event carries. Either a relative change for EV_REL,
  4930. // absolute new value for EV_ABS (joysticks ...), or 0 for EV_KEY for release, 1 for keypress and 2 for autorepeat
  4931. CORE.Input.Keyboard.currentKeyState[keycode] = (event.value >= 1)? 1 : 0;
  4932. if (event.value >= 1)
  4933. {
  4934. CORE.Input.Keyboard.keyPressedQueue[CORE.Input.Keyboard.keyPressedQueueCount] = keycode; // Register last key pressed
  4935. CORE.Input.Keyboard.keyPressedQueueCount++;
  4936. }
  4937. #if defined(SUPPORT_SCREEN_CAPTURE)
  4938. // Check screen capture key (raylib key: KEY_F12)
  4939. if (CORE.Input.Keyboard.currentKeyState[301] == 1)
  4940. {
  4941. TakeScreenshot(TextFormat("screenshot%03i.png", screenshotCounter));
  4942. screenshotCounter++;
  4943. }
  4944. #endif
  4945. if (CORE.Input.Keyboard.currentKeyState[CORE.Input.Keyboard.exitKey] == 1) CORE.Window.shouldClose = true;
  4946. TRACELOGD("RPI: KEY_%s ScanCode: %4i KeyCode: %4i", event.value == 0 ? "UP":"DOWN", event.code, keycode);
  4947. }
  4948. }
  4949. }
  4950. }
  4951. }
  4952. // Input device events reading thread
  4953. static void *EventThread(void *arg)
  4954. {
  4955. struct input_event event = { 0 };
  4956. InputEventWorker *worker = (InputEventWorker *)arg;
  4957. int touchAction = -1; // 0-TOUCH_ACTION_UP, 1-TOUCH_ACTION_DOWN, 2-TOUCH_ACTION_MOVE
  4958. bool gestureUpdate = false; // Flag to note gestures require to update
  4959. while (!CORE.Window.shouldClose)
  4960. {
  4961. // Try to read data from the device and only continue if successful
  4962. while (read(worker->fd, &event, sizeof(event)) == (int)sizeof(event))
  4963. {
  4964. // Relative movement parsing
  4965. if (event.type == EV_REL)
  4966. {
  4967. if (event.code == REL_X)
  4968. {
  4969. CORE.Input.Mouse.currentPosition.x += event.value;
  4970. CORE.Input.Touch.position[0].x = CORE.Input.Mouse.currentPosition.x;
  4971. touchAction = 2; // TOUCH_ACTION_MOVE
  4972. gestureUpdate = true;
  4973. }
  4974. if (event.code == REL_Y)
  4975. {
  4976. CORE.Input.Mouse.currentPosition.y += event.value;
  4977. CORE.Input.Touch.position[0].y = CORE.Input.Mouse.currentPosition.y;
  4978. touchAction = 2; // TOUCH_ACTION_MOVE
  4979. gestureUpdate = true;
  4980. }
  4981. if (event.code == REL_WHEEL) CORE.Input.Mouse.currentWheelMove += event.value;
  4982. }
  4983. // Absolute movement parsing
  4984. if (event.type == EV_ABS)
  4985. {
  4986. // Basic movement
  4987. if (event.code == ABS_X)
  4988. {
  4989. CORE.Input.Mouse.currentPosition.x = (event.value - worker->absRange.x)*CORE.Window.screen.width/worker->absRange.width; // Scale acording to absRange
  4990. CORE.Input.Touch.position[0].x = (event.value - worker->absRange.x)*CORE.Window.screen.width/worker->absRange.width; // Scale acording to absRange
  4991. touchAction = 2; // TOUCH_ACTION_MOVE
  4992. gestureUpdate = true;
  4993. }
  4994. if (event.code == ABS_Y)
  4995. {
  4996. CORE.Input.Mouse.currentPosition.y = (event.value - worker->absRange.y)*CORE.Window.screen.height/worker->absRange.height; // Scale acording to absRange
  4997. CORE.Input.Touch.position[0].y = (event.value - worker->absRange.y)*CORE.Window.screen.height/worker->absRange.height; // Scale acording to absRange
  4998. touchAction = 2; // TOUCH_ACTION_MOVE
  4999. gestureUpdate = true;
  5000. }
  5001. // Multitouch movement
  5002. if (event.code == ABS_MT_SLOT) worker->touchSlot = event.value; // Remember the slot number for the folowing events
  5003. if (event.code == ABS_MT_POSITION_X)
  5004. {
  5005. if (worker->touchSlot < MAX_TOUCH_POINTS) CORE.Input.Touch.position[worker->touchSlot].x = (event.value - worker->absRange.x)*CORE.Window.screen.width/worker->absRange.width; // Scale acording to absRange
  5006. }
  5007. if (event.code == ABS_MT_POSITION_Y)
  5008. {
  5009. if (worker->touchSlot < MAX_TOUCH_POINTS) CORE.Input.Touch.position[worker->touchSlot].y = (event.value - worker->absRange.y)*CORE.Window.screen.height/worker->absRange.height; // Scale acording to absRange
  5010. }
  5011. if (event.code == ABS_MT_TRACKING_ID)
  5012. {
  5013. if ((event.value < 0) && (worker->touchSlot < MAX_TOUCH_POINTS))
  5014. {
  5015. // Touch has ended for this point
  5016. CORE.Input.Touch.position[worker->touchSlot].x = -1;
  5017. CORE.Input.Touch.position[worker->touchSlot].y = -1;
  5018. }
  5019. }
  5020. // Touchscreen tap
  5021. if (event.code == ABS_PRESSURE)
  5022. {
  5023. int previousMouseLeftButtonState = CORE.Input.Mouse.currentButtonStateEvdev[MOUSE_BUTTON_LEFT];
  5024. if (!event.value && previousMouseLeftButtonState)
  5025. {
  5026. CORE.Input.Mouse.currentButtonStateEvdev[MOUSE_BUTTON_LEFT] = 0;
  5027. touchAction = 0; // TOUCH_ACTION_UP
  5028. gestureUpdate = true;
  5029. }
  5030. if (event.value && !previousMouseLeftButtonState)
  5031. {
  5032. CORE.Input.Mouse.currentButtonStateEvdev[MOUSE_BUTTON_LEFT] = 1;
  5033. touchAction = 1; // TOUCH_ACTION_DOWN
  5034. gestureUpdate = true;
  5035. }
  5036. }
  5037. }
  5038. // Button parsing
  5039. if (event.type == EV_KEY)
  5040. {
  5041. // Mouse button parsing
  5042. if ((event.code == BTN_TOUCH) || (event.code == BTN_LEFT))
  5043. {
  5044. CORE.Input.Mouse.currentButtonStateEvdev[MOUSE_BUTTON_LEFT] = event.value;
  5045. if (event.value > 0) touchAction = 1; // TOUCH_ACTION_DOWN
  5046. else touchAction = 0; // TOUCH_ACTION_UP
  5047. gestureUpdate = true;
  5048. }
  5049. if (event.code == BTN_RIGHT) CORE.Input.Mouse.currentButtonStateEvdev[MOUSE_BUTTON_RIGHT] = event.value;
  5050. if (event.code == BTN_MIDDLE) CORE.Input.Mouse.currentButtonStateEvdev[MOUSE_BUTTON_MIDDLE] = event.value;
  5051. if (event.code == BTN_SIDE) CORE.Input.Mouse.currentButtonStateEvdev[MOUSE_BUTTON_SIDE] = event.value;
  5052. if (event.code == BTN_EXTRA) CORE.Input.Mouse.currentButtonStateEvdev[MOUSE_BUTTON_EXTRA] = event.value;
  5053. if (event.code == BTN_FORWARD) CORE.Input.Mouse.currentButtonStateEvdev[MOUSE_BUTTON_FORWARD] = event.value;
  5054. if (event.code == BTN_BACK) CORE.Input.Mouse.currentButtonStateEvdev[MOUSE_BUTTON_BACK] = event.value;
  5055. }
  5056. // Screen confinement
  5057. if (!CORE.Input.Mouse.cursorHidden)
  5058. {
  5059. if (CORE.Input.Mouse.currentPosition.x < 0) CORE.Input.Mouse.currentPosition.x = 0;
  5060. if (CORE.Input.Mouse.currentPosition.x > CORE.Window.screen.width/CORE.Input.Mouse.scale.x) CORE.Input.Mouse.currentPosition.x = CORE.Window.screen.width/CORE.Input.Mouse.scale.x;
  5061. if (CORE.Input.Mouse.currentPosition.y < 0) CORE.Input.Mouse.currentPosition.y = 0;
  5062. if (CORE.Input.Mouse.currentPosition.y > CORE.Window.screen.height/CORE.Input.Mouse.scale.y) CORE.Input.Mouse.currentPosition.y = CORE.Window.screen.height/CORE.Input.Mouse.scale.y;
  5063. }
  5064. #if defined(SUPPORT_GESTURES_SYSTEM) // PLATFORM_RPI, PLATFORM_DRM
  5065. if (gestureUpdate)
  5066. {
  5067. GestureEvent gestureEvent = { 0 };
  5068. gestureEvent.pointCount = 0;
  5069. gestureEvent.touchAction = touchAction;
  5070. if (CORE.Input.Touch.position[0].x >= 0) gestureEvent.pointCount++;
  5071. if (CORE.Input.Touch.position[1].x >= 0) gestureEvent.pointCount++;
  5072. if (CORE.Input.Touch.position[2].x >= 0) gestureEvent.pointCount++;
  5073. if (CORE.Input.Touch.position[3].x >= 0) gestureEvent.pointCount++;
  5074. gestureEvent.pointId[0] = 0;
  5075. gestureEvent.pointId[1] = 1;
  5076. gestureEvent.pointId[2] = 2;
  5077. gestureEvent.pointId[3] = 3;
  5078. gestureEvent.position[0] = CORE.Input.Touch.position[0];
  5079. gestureEvent.position[1] = CORE.Input.Touch.position[1];
  5080. gestureEvent.position[2] = CORE.Input.Touch.position[2];
  5081. gestureEvent.position[3] = CORE.Input.Touch.position[3];
  5082. ProcessGestureEvent(gestureEvent);
  5083. }
  5084. #endif
  5085. }
  5086. WaitTime(5); // Sleep for 5ms to avoid hogging CPU time
  5087. }
  5088. close(worker->fd);
  5089. return NULL;
  5090. }
  5091. // Initialize gamepad system
  5092. static void InitGamepad(void)
  5093. {
  5094. char gamepadDev[128] = { 0 };
  5095. for (int i = 0; i < MAX_GAMEPADS; i++)
  5096. {
  5097. sprintf(gamepadDev, "%s%i", DEFAULT_GAMEPAD_DEV, i);
  5098. if ((CORE.Input.Gamepad.streamId[i] = open(gamepadDev, O_RDONLY|O_NONBLOCK)) < 0)
  5099. {
  5100. // NOTE: Only show message for first gamepad
  5101. if (i == 0) TRACELOG(LOG_WARNING, "RPI: Failed to open Gamepad device, no gamepad available");
  5102. }
  5103. else
  5104. {
  5105. CORE.Input.Gamepad.ready[i] = true;
  5106. // NOTE: Only create one thread
  5107. if (i == 0)
  5108. {
  5109. int error = pthread_create(&CORE.Input.Gamepad.threadId, NULL, &GamepadThread, NULL);
  5110. if (error != 0) TRACELOG(LOG_WARNING, "RPI: Failed to create gamepad input event thread");
  5111. else TRACELOG(LOG_INFO, "RPI: Gamepad device initialized successfully");
  5112. }
  5113. }
  5114. }
  5115. }
  5116. // Process Gamepad (/dev/input/js0)
  5117. static void *GamepadThread(void *arg)
  5118. {
  5119. #define JS_EVENT_BUTTON 0x01 // Button pressed/released
  5120. #define JS_EVENT_AXIS 0x02 // Joystick axis moved
  5121. #define JS_EVENT_INIT 0x80 // Initial state of device
  5122. struct js_event {
  5123. unsigned int time; // event timestamp in milliseconds
  5124. short value; // event value
  5125. unsigned char type; // event type
  5126. unsigned char number; // event axis/button number
  5127. };
  5128. // Read gamepad event
  5129. struct js_event gamepadEvent = { 0 };
  5130. while (!CORE.Window.shouldClose)
  5131. {
  5132. for (int i = 0; i < MAX_GAMEPADS; i++)
  5133. {
  5134. if (read(CORE.Input.Gamepad.streamId[i], &gamepadEvent, sizeof(struct js_event)) == (int)sizeof(struct js_event))
  5135. {
  5136. gamepadEvent.type &= ~JS_EVENT_INIT; // Ignore synthetic events
  5137. // Process gamepad events by type
  5138. if (gamepadEvent.type == JS_EVENT_BUTTON)
  5139. {
  5140. //TRACELOG(LOG_WARNING, "RPI: Gamepad button: %i, value: %i", gamepadEvent.number, gamepadEvent.value);
  5141. if (gamepadEvent.number < MAX_GAMEPAD_BUTTONS)
  5142. {
  5143. // 1 - button pressed, 0 - button released
  5144. CORE.Input.Gamepad.currentButtonState[i][gamepadEvent.number] = (int)gamepadEvent.value;
  5145. if ((int)gamepadEvent.value == 1) CORE.Input.Gamepad.lastButtonPressed = gamepadEvent.number;
  5146. else CORE.Input.Gamepad.lastButtonPressed = -1;
  5147. }
  5148. }
  5149. else if (gamepadEvent.type == JS_EVENT_AXIS)
  5150. {
  5151. //TRACELOG(LOG_WARNING, "RPI: Gamepad axis: %i, value: %i", gamepadEvent.number, gamepadEvent.value);
  5152. if (gamepadEvent.number < MAX_GAMEPAD_AXIS)
  5153. {
  5154. // NOTE: Scaling of gamepadEvent.value to get values between -1..1
  5155. CORE.Input.Gamepad.axisState[i][gamepadEvent.number] = (float)gamepadEvent.value/32768;
  5156. }
  5157. }
  5158. }
  5159. else WaitTime(1); // Sleep for 1 ms to avoid hogging CPU time
  5160. }
  5161. }
  5162. return NULL;
  5163. }
  5164. #endif // PLATFORM_RPI || PLATFORM_DRM
  5165. #if defined(PLATFORM_DRM)
  5166. // Search matching DRM mode in connector's mode list
  5167. static int FindMatchingConnectorMode(const drmModeConnector *connector, const drmModeModeInfo *mode)
  5168. {
  5169. if (NULL == connector) return -1;
  5170. if (NULL == mode) return -1;
  5171. // safe bitwise comparison of two modes
  5172. #define BINCMP(a, b) memcmp((a), (b), (sizeof(a) < sizeof(b)) ? sizeof(a) : sizeof(b))
  5173. for (size_t i = 0; i < connector->count_modes; i++)
  5174. {
  5175. TRACELOG(LOG_TRACE, "DISPLAY: DRM mode: %d %ux%u@%u %s", i, connector->modes[i].hdisplay, connector->modes[i].vdisplay,
  5176. connector->modes[i].vrefresh, (connector->modes[i].flags & DRM_MODE_FLAG_INTERLACE) ? "interlaced" : "progressive");
  5177. if (0 == BINCMP(&CORE.Window.crtc->mode, &CORE.Window.connector->modes[i])) return i;
  5178. }
  5179. return -1;
  5180. #undef BINCMP
  5181. }
  5182. // Search exactly matching DRM connector mode in connector's list
  5183. static int FindExactConnectorMode(const drmModeConnector *connector, uint width, uint height, uint fps, bool allowInterlaced)
  5184. {
  5185. TRACELOG(LOG_TRACE, "DISPLAY: Searching exact connector mode for %ux%u@%u, selecting an interlaced mode is allowed: %s", width, height, fps, allowInterlaced ? "yes" : "no");
  5186. if (NULL == connector) return -1;
  5187. for (int i = 0; i < CORE.Window.connector->count_modes; i++)
  5188. {
  5189. const drmModeModeInfo *const mode = &CORE.Window.connector->modes[i];
  5190. TRACELOG(LOG_TRACE, "DISPLAY: DRM Mode %d %ux%u@%u %s", i, mode->hdisplay, mode->vdisplay, mode->vrefresh, (mode->flags & DRM_MODE_FLAG_INTERLACE) ? "interlaced" : "progressive");
  5191. if ((mode->flags & DRM_MODE_FLAG_INTERLACE) && (!allowInterlaced)) continue;
  5192. if ((mode->hdisplay == width) && (mode->vdisplay == height) && (mode->vrefresh == fps)) return i;
  5193. }
  5194. TRACELOG(LOG_TRACE, "DISPLAY: No DRM exact matching mode found");
  5195. return -1;
  5196. }
  5197. // Search the nearest matching DRM connector mode in connector's list
  5198. static int FindNearestConnectorMode(const drmModeConnector *connector, uint width, uint height, uint fps, bool allowInterlaced)
  5199. {
  5200. TRACELOG(LOG_TRACE, "DISPLAY: Searching nearest connector mode for %ux%u@%u, selecting an interlaced mode is allowed: %s", width, height, fps, allowInterlaced ? "yes" : "no");
  5201. if (NULL == connector) return -1;
  5202. int nearestIndex = -1;
  5203. for (int i = 0; i < CORE.Window.connector->count_modes; i++)
  5204. {
  5205. const drmModeModeInfo *const mode = &CORE.Window.connector->modes[i];
  5206. TRACELOG(LOG_TRACE, "DISPLAY: DRM mode: %d %ux%u@%u %s", i, mode->hdisplay, mode->vdisplay, mode->vrefresh,
  5207. (mode->flags & DRM_MODE_FLAG_INTERLACE) ? "interlaced" : "progressive");
  5208. if ((mode->hdisplay < width) || (mode->vdisplay < height) | (mode->vrefresh < fps))
  5209. {
  5210. TRACELOG(LOG_TRACE, "DISPLAY: DRM mode is too small");
  5211. continue;
  5212. }
  5213. if ((mode->flags & DRM_MODE_FLAG_INTERLACE) && (!allowInterlaced))
  5214. {
  5215. TRACELOG(LOG_TRACE, "DISPLAY: DRM shouldn't choose an interlaced mode");
  5216. continue;
  5217. }
  5218. if ((mode->hdisplay >= width) && (mode->vdisplay >= height) && (mode->vrefresh >= fps))
  5219. {
  5220. const int widthDiff = mode->hdisplay - width;
  5221. const int heightDiff = mode->vdisplay - height;
  5222. const int fpsDiff = mode->vrefresh - fps;
  5223. if (nearestIndex < 0)
  5224. {
  5225. nearestIndex = i;
  5226. continue;
  5227. }
  5228. const int nearestWidthDiff = CORE.Window.connector->modes[nearestIndex].hdisplay - width;
  5229. const int nearestHeightDiff = CORE.Window.connector->modes[nearestIndex].vdisplay - height;
  5230. const int nearestFpsDiff = CORE.Window.connector->modes[nearestIndex].vrefresh - fps;
  5231. if ((widthDiff < nearestWidthDiff) || (heightDiff < nearestHeightDiff) || (fpsDiff < nearestFpsDiff)) nearestIndex = i;
  5232. }
  5233. }
  5234. return nearestIndex;
  5235. }
  5236. #endif
  5237. #if defined(SUPPORT_EVENTS_AUTOMATION)
  5238. // NOTE: Loading happens over AutomationEvent *events
  5239. static void LoadAutomationEvents(const char *fileName)
  5240. {
  5241. //unsigned char fileId[4] = { 0 };
  5242. // Load binary
  5243. /*
  5244. FILE *repFile = fopen(fileName, "rb");
  5245. fread(fileId, 4, 1, repFile);
  5246. if ((fileId[0] == 'r') && (fileId[1] == 'E') && (fileId[2] == 'P') && (fileId[1] == ' '))
  5247. {
  5248. fread(&eventCount, sizeof(int), 1, repFile);
  5249. TraceLog(LOG_WARNING, "Events loaded: %i\n", eventCount);
  5250. fread(events, sizeof(AutomationEvent), eventCount, repFile);
  5251. }
  5252. fclose(repFile);
  5253. */
  5254. // Load events (text file)
  5255. FILE *repFile = fopen(fileName, "rt");
  5256. if (repFile != NULL)
  5257. {
  5258. unsigned int count = 0;
  5259. char buffer[256] = { 0 };
  5260. fgets(buffer, 256, repFile);
  5261. while (!feof(repFile))
  5262. {
  5263. if (buffer[0] == 'c') sscanf(buffer, "c %i", &eventCount);
  5264. else if (buffer[0] == 'e')
  5265. {
  5266. sscanf(buffer, "e %d %d %d %d %d", &events[count].frame, &events[count].type,
  5267. &events[count].params[0], &events[count].params[1], &events[count].params[2]);
  5268. count++;
  5269. }
  5270. fgets(buffer, 256, repFile);
  5271. }
  5272. if (count != eventCount) TRACELOG(LOG_WARNING, "Events count provided is different than count");
  5273. fclose(repFile);
  5274. }
  5275. TRACELOG(LOG_WARNING, "Events loaded: %i", eventCount);
  5276. }
  5277. // Export recorded events into a file
  5278. static void ExportAutomationEvents(const char *fileName)
  5279. {
  5280. // TODO: eventCount is required -> header? -> rAEL
  5281. unsigned char fileId[4] = "rEP ";
  5282. // Save as binary
  5283. /*
  5284. FILE *repFile = fopen(fileName, "wb");
  5285. fwrite(fileId, 4, 1, repFile);
  5286. fwrite(&eventCount, sizeof(int), 1, repFile);
  5287. fwrite(events, sizeof(AutomationEvent), eventCount, repFile);
  5288. fclose(repFile);
  5289. */
  5290. // Export events as text
  5291. FILE *repFile = fopen(fileName, "wt");
  5292. if (repFile != NULL)
  5293. {
  5294. fprintf(repFile, "# Automation events list\n");
  5295. fprintf(repFile, "# c <events_count>\n");
  5296. fprintf(repFile, "# e <frame> <event_type> <param0> <param1> <param2> // <event_type_name>\n");
  5297. fprintf(repFile, "c %i\n", eventCount);
  5298. for (int i = 0; i < eventCount; i++)
  5299. {
  5300. fprintf(repFile, "e %i %i %i %i %i // %s\n", events[i].frame, events[i].type,
  5301. events[i].params[0], events[i].params[1], events[i].params[2], autoEventTypeName[events[i].type]);
  5302. }
  5303. fclose(repFile);
  5304. }
  5305. }
  5306. // EndDrawing() -> After PollInputEvents()
  5307. // Check event in current frame and save into the events[i] array
  5308. static void RecordAutomationEvent(unsigned int frame)
  5309. {
  5310. for (int key = 0; key < MAX_KEYBOARD_KEYS; key++)
  5311. {
  5312. // INPUT_KEY_UP (only saved once)
  5313. if (CORE.Input.Keyboard.previousKeyState[key] && !CORE.Input.Keyboard.currentKeyState[key])
  5314. {
  5315. events[eventCount].frame = frame;
  5316. events[eventCount].type = INPUT_KEY_UP;
  5317. events[eventCount].params[0] = key;
  5318. events[eventCount].params[1] = 0;
  5319. events[eventCount].params[2] = 0;
  5320. TRACELOG(LOG_INFO, "[%i] INPUT_KEY_UP: %i, %i, %i", events[eventCount].frame, events[eventCount].params[0], events[eventCount].params[1], events[eventCount].params[2]);
  5321. eventCount++;
  5322. }
  5323. // INPUT_KEY_DOWN
  5324. if (CORE.Input.Keyboard.currentKeyState[key])
  5325. {
  5326. events[eventCount].frame = frame;
  5327. events[eventCount].type = INPUT_KEY_DOWN;
  5328. events[eventCount].params[0] = key;
  5329. events[eventCount].params[1] = 0;
  5330. events[eventCount].params[2] = 0;
  5331. TRACELOG(LOG_INFO, "[%i] INPUT_KEY_DOWN: %i, %i, %i", events[eventCount].frame, events[eventCount].params[0], events[eventCount].params[1], events[eventCount].params[2]);
  5332. eventCount++;
  5333. }
  5334. }
  5335. for (int button = 0; button < MAX_MOUSE_BUTTONS; button++)
  5336. {
  5337. // INPUT_MOUSE_BUTTON_UP
  5338. if (CORE.Input.Mouse.previousButtonState[button] && !CORE.Input.Mouse.currentButtonState[button])
  5339. {
  5340. events[eventCount].frame = frame;
  5341. events[eventCount].type = INPUT_MOUSE_BUTTON_UP;
  5342. events[eventCount].params[0] = button;
  5343. events[eventCount].params[1] = 0;
  5344. events[eventCount].params[2] = 0;
  5345. TRACELOG(LOG_INFO, "[%i] INPUT_MOUSE_BUTTON_UP: %i, %i, %i", events[eventCount].frame, events[eventCount].params[0], events[eventCount].params[1], events[eventCount].params[2]);
  5346. eventCount++;
  5347. }
  5348. // INPUT_MOUSE_BUTTON_DOWN
  5349. if (CORE.Input.Mouse.currentButtonState[button])
  5350. {
  5351. events[eventCount].frame = frame;
  5352. events[eventCount].type = INPUT_MOUSE_BUTTON_DOWN;
  5353. events[eventCount].params[0] = button;
  5354. events[eventCount].params[1] = 0;
  5355. events[eventCount].params[2] = 0;
  5356. TRACELOG(LOG_INFO, "[%i] INPUT_MOUSE_BUTTON_DOWN: %i, %i, %i", events[eventCount].frame, events[eventCount].params[0], events[eventCount].params[1], events[eventCount].params[2]);
  5357. eventCount++;
  5358. }
  5359. }
  5360. // INPUT_MOUSE_POSITION (only saved if changed)
  5361. if (((int)CORE.Input.Mouse.currentPosition.x != (int)CORE.Input.Mouse.previousPosition.x) ||
  5362. ((int)CORE.Input.Mouse.currentPosition.y != (int)CORE.Input.Mouse.previousPosition.y))
  5363. {
  5364. events[eventCount].frame = frame;
  5365. events[eventCount].type = INPUT_MOUSE_POSITION;
  5366. events[eventCount].params[0] = (int)CORE.Input.Mouse.currentPosition.x;
  5367. events[eventCount].params[1] = (int)CORE.Input.Mouse.currentPosition.y;
  5368. events[eventCount].params[2] = 0;
  5369. TRACELOG(LOG_INFO, "[%i] INPUT_MOUSE_POSITION: %i, %i, %i", events[eventCount].frame, events[eventCount].params[0], events[eventCount].params[1], events[eventCount].params[2]);
  5370. eventCount++;
  5371. }
  5372. // INPUT_MOUSE_WHEEL_MOTION
  5373. if ((int)CORE.Input.Mouse.currentWheelMove != (int)CORE.Input.Mouse.previousWheelMove)
  5374. {
  5375. events[eventCount].frame = frame;
  5376. events[eventCount].type = INPUT_MOUSE_WHEEL_MOTION;
  5377. events[eventCount].params[0] = (int)CORE.Input.Mouse.currentWheelMove;
  5378. events[eventCount].params[1] = 0;
  5379. events[eventCount].params[2] = 0;
  5380. TRACELOG(LOG_INFO, "[%i] INPUT_MOUSE_WHEEL_MOTION: %i, %i, %i", events[eventCount].frame, events[eventCount].params[0], events[eventCount].params[1], events[eventCount].params[2]);
  5381. eventCount++;
  5382. }
  5383. for (int id = 0; id < MAX_TOUCH_POINTS; id++)
  5384. {
  5385. // INPUT_TOUCH_UP
  5386. if (CORE.Input.Touch.previousTouchState[id] && !CORE.Input.Touch.currentTouchState[id])
  5387. {
  5388. events[eventCount].frame = frame;
  5389. events[eventCount].type = INPUT_TOUCH_UP;
  5390. events[eventCount].params[0] = id;
  5391. events[eventCount].params[1] = 0;
  5392. events[eventCount].params[2] = 0;
  5393. TRACELOG(LOG_INFO, "[%i] INPUT_TOUCH_UP: %i, %i, %i", events[eventCount].frame, events[eventCount].params[0], events[eventCount].params[1], events[eventCount].params[2]);
  5394. eventCount++;
  5395. }
  5396. // INPUT_TOUCH_DOWN
  5397. if (CORE.Input.Touch.currentTouchState[id])
  5398. {
  5399. events[eventCount].frame = frame;
  5400. events[eventCount].type = INPUT_TOUCH_DOWN;
  5401. events[eventCount].params[0] = id;
  5402. events[eventCount].params[1] = 0;
  5403. events[eventCount].params[2] = 0;
  5404. TRACELOG(LOG_INFO, "[%i] INPUT_TOUCH_DOWN: %i, %i, %i", events[eventCount].frame, events[eventCount].params[0], events[eventCount].params[1], events[eventCount].params[2]);
  5405. eventCount++;
  5406. }
  5407. // INPUT_TOUCH_POSITION
  5408. // TODO: It requires the id!
  5409. /*
  5410. if (((int)CORE.Input.Touch.currentPosition[id].x != (int)CORE.Input.Touch.previousPosition[id].x) ||
  5411. ((int)CORE.Input.Touch.currentPosition[id].y != (int)CORE.Input.Touch.previousPosition[id].y))
  5412. {
  5413. events[eventCount].frame = frame;
  5414. events[eventCount].type = INPUT_TOUCH_POSITION;
  5415. events[eventCount].params[0] = id;
  5416. events[eventCount].params[1] = (int)CORE.Input.Touch.currentPosition[id].x;
  5417. events[eventCount].params[2] = (int)CORE.Input.Touch.currentPosition[id].y;
  5418. TRACELOG(LOG_INFO, "[%i] INPUT_TOUCH_POSITION: %i, %i, %i", events[eventCount].frame, events[eventCount].params[0], events[eventCount].params[1], events[eventCount].params[2]);
  5419. eventCount++;
  5420. }
  5421. */
  5422. }
  5423. for (int gamepad = 0; gamepad < MAX_GAMEPADS; gamepad++)
  5424. {
  5425. // INPUT_GAMEPAD_CONNECT
  5426. /*
  5427. if ((CORE.Input.Gamepad.currentState[gamepad] != CORE.Input.Gamepad.previousState[gamepad]) &&
  5428. (CORE.Input.Gamepad.currentState[gamepad] == true)) // Check if changed to ready
  5429. {
  5430. // TODO: Save gamepad connect event
  5431. }
  5432. */
  5433. // INPUT_GAMEPAD_DISCONNECT
  5434. /*
  5435. if ((CORE.Input.Gamepad.currentState[gamepad] != CORE.Input.Gamepad.previousState[gamepad]) &&
  5436. (CORE.Input.Gamepad.currentState[gamepad] == false)) // Check if changed to not-ready
  5437. {
  5438. // TODO: Save gamepad disconnect event
  5439. }
  5440. */
  5441. for (int button = 0; button < MAX_GAMEPAD_BUTTONS; button++)
  5442. {
  5443. // INPUT_GAMEPAD_BUTTON_UP
  5444. if (CORE.Input.Gamepad.previousButtonState[gamepad][button] && !CORE.Input.Gamepad.currentButtonState[gamepad][button])
  5445. {
  5446. events[eventCount].frame = frame;
  5447. events[eventCount].type = INPUT_GAMEPAD_BUTTON_UP;
  5448. events[eventCount].params[0] = gamepad;
  5449. events[eventCount].params[1] = button;
  5450. events[eventCount].params[2] = 0;
  5451. TRACELOG(LOG_INFO, "[%i] INPUT_GAMEPAD_BUTTON_UP: %i, %i, %i", events[eventCount].frame, events[eventCount].params[0], events[eventCount].params[1], events[eventCount].params[2]);
  5452. eventCount++;
  5453. }
  5454. // INPUT_GAMEPAD_BUTTON_DOWN
  5455. if (CORE.Input.Gamepad.currentButtonState[gamepad][button])
  5456. {
  5457. events[eventCount].frame = frame;
  5458. events[eventCount].type = INPUT_GAMEPAD_BUTTON_DOWN;
  5459. events[eventCount].params[0] = gamepad;
  5460. events[eventCount].params[1] = button;
  5461. events[eventCount].params[2] = 0;
  5462. TRACELOG(LOG_INFO, "[%i] INPUT_GAMEPAD_BUTTON_DOWN: %i, %i, %i", events[eventCount].frame, events[eventCount].params[0], events[eventCount].params[1], events[eventCount].params[2]);
  5463. eventCount++;
  5464. }
  5465. }
  5466. for (int axis = 0; axis < MAX_GAMEPAD_AXIS; axis++)
  5467. {
  5468. // INPUT_GAMEPAD_AXIS_MOTION
  5469. if (CORE.Input.Gamepad.axisState[gamepad][axis] > 0.1f)
  5470. {
  5471. events[eventCount].frame = frame;
  5472. events[eventCount].type = INPUT_GAMEPAD_AXIS_MOTION;
  5473. events[eventCount].params[0] = gamepad;
  5474. events[eventCount].params[1] = axis;
  5475. events[eventCount].params[2] = (int)(CORE.Input.Gamepad.axisState[gamepad][axis]*32768.0f);
  5476. TRACELOG(LOG_INFO, "[%i] INPUT_GAMEPAD_AXIS_MOTION: %i, %i, %i", events[eventCount].frame, events[eventCount].params[0], events[eventCount].params[1], events[eventCount].params[2]);
  5477. eventCount++;
  5478. }
  5479. }
  5480. }
  5481. // INPUT_GESTURE
  5482. if (GESTURES.current != GESTURE_NONE)
  5483. {
  5484. events[eventCount].frame = frame;
  5485. events[eventCount].type = INPUT_GESTURE;
  5486. events[eventCount].params[0] = GESTURES.current;
  5487. events[eventCount].params[1] = 0;
  5488. events[eventCount].params[2] = 0;
  5489. TRACELOG(LOG_INFO, "[%i] INPUT_GESTURE: %i, %i, %i", events[eventCount].frame, events[eventCount].params[0], events[eventCount].params[1], events[eventCount].params[2]);
  5490. eventCount++;
  5491. }
  5492. }
  5493. // Play automation event
  5494. static void PlayAutomationEvent(unsigned int frame)
  5495. {
  5496. for (unsigned int i = 0; i < eventCount; i++)
  5497. {
  5498. if (events[i].frame == frame)
  5499. {
  5500. switch (events[i].type)
  5501. {
  5502. // Input events
  5503. case INPUT_KEY_UP: CORE.Input.Keyboard.currentKeyState[events[i].params[0]] = false; break; // param[0]: key
  5504. case INPUT_KEY_DOWN: CORE.Input.Keyboard.currentKeyState[events[i].params[0]] = true; break; // param[0]: key
  5505. case INPUT_MOUSE_BUTTON_UP: CORE.Input.Mouse.currentButtonState[events[i].params[0]] = false; break; // param[0]: key
  5506. case INPUT_MOUSE_BUTTON_DOWN: CORE.Input.Mouse.currentButtonState[events[i].params[0]] = true; break; // param[0]: key
  5507. case INPUT_MOUSE_POSITION: // param[0]: x, param[1]: y
  5508. {
  5509. CORE.Input.Mouse.currentPosition.x = (float)events[i].params[0];
  5510. CORE.Input.Mouse.currentPosition.y = (float)events[i].params[1];
  5511. } break;
  5512. case INPUT_MOUSE_WHEEL_MOTION: CORE.Input.Mouse.currentWheelMove = (float)events[i].params[0]; break; // param[0]: delta
  5513. case INPUT_TOUCH_UP: CORE.Input.Touch.currentTouchState[events[i].params[0]] = false; break; // param[0]: id
  5514. case INPUT_TOUCH_DOWN: CORE.Input.Touch.currentTouchState[events[i].params[0]] = true; break; // param[0]: id
  5515. case INPUT_TOUCH_POSITION: // param[0]: id, param[1]: x, param[2]: y
  5516. {
  5517. CORE.Input.Touch.position[events[i].params[0]].x = (float)events[i].params[1];
  5518. CORE.Input.Touch.position[events[i].params[0]].y = (float)events[i].params[2];
  5519. } break;
  5520. case INPUT_GAMEPAD_CONNECT: CORE.Input.Gamepad.ready[events[i].params[0]] = true; break; // param[0]: gamepad
  5521. case INPUT_GAMEPAD_DISCONNECT: CORE.Input.Gamepad.ready[events[i].params[0]] = false; break; // param[0]: gamepad
  5522. case INPUT_GAMEPAD_BUTTON_UP: CORE.Input.Gamepad.currentButtonState[events[i].params[0]][events[i].params[1]] = false; break; // param[0]: gamepad, param[1]: button
  5523. case INPUT_GAMEPAD_BUTTON_DOWN: CORE.Input.Gamepad.currentButtonState[events[i].params[0]][events[i].params[1]] = true; break; // param[0]: gamepad, param[1]: button
  5524. case INPUT_GAMEPAD_AXIS_MOTION: // param[0]: gamepad, param[1]: axis, param[2]: delta
  5525. {
  5526. CORE.Input.Gamepad.axisState[events[i].params[0]][events[i].params[1]] = ((float)events[i].params[2]/32768.0f);
  5527. } break;
  5528. case INPUT_GESTURE: GESTURES.current = events[i].params[0]; break; // param[0]: gesture (enum Gesture) -> gestures.h: GESTURES.current
  5529. // Window events
  5530. case WINDOW_CLOSE: CORE.Window.shouldClose = true; break;
  5531. case WINDOW_MAXIMIZE: MaximizeWindow(); break;
  5532. case WINDOW_MINIMIZE: MinimizeWindow(); break;
  5533. case WINDOW_RESIZE: SetWindowSize(events[i].params[0], events[i].params[1]); break;
  5534. // Custom events
  5535. case ACTION_TAKE_SCREENSHOT:
  5536. {
  5537. TakeScreenshot(TextFormat("screenshot%03i.png", screenshotCounter));
  5538. screenshotCounter++;
  5539. } break;
  5540. case ACTION_SETTARGETFPS: SetTargetFPS(events[i].params[0]); break;
  5541. default: break;
  5542. }
  5543. }
  5544. }
  5545. }
  5546. #endif