sokol_app.h 478 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311331233133314331533163317331833193320332133223323332433253326332733283329333033313332333333343335333633373338333933403341334233433344334533463347334833493350335133523353335433553356335733583359336033613362336333643365336633673368336933703371337233733374337533763377337833793380338133823383338433853386338733883389339033913392339333943395339633973398339934003401340234033404340534063407340834093410341134123413341434153416341734183419342034213422342334243425342634273428342934303431343234333434343534363437343834393440344134423443344434453446344734483449345034513452345334543455345634573458345934603461346234633464346534663467346834693470347134723473347434753476347734783479348034813482348334843485348634873488348934903491349234933494349534963497349834993500350135023503350435053506350735083509351035113512351335143515351635173518351935203521352235233524352535263527352835293530353135323533353435353536353735383539354035413542354335443545354635473548354935503551355235533554355535563557355835593560356135623563356435653566356735683569357035713572357335743575357635773578357935803581358235833584358535863587358835893590359135923593359435953596359735983599360036013602360336043605360636073608360936103611361236133614361536163617361836193620362136223623362436253626362736283629363036313632363336343635363636373638363936403641364236433644364536463647364836493650365136523653365436553656365736583659366036613662366336643665366636673668366936703671367236733674367536763677367836793680368136823683368436853686368736883689369036913692369336943695369636973698369937003701370237033704370537063707370837093710371137123713371437153716371737183719372037213722372337243725372637273728372937303731373237333734373537363737373837393740374137423743374437453746374737483749375037513752375337543755375637573758375937603761376237633764376537663767376837693770377137723773377437753776377737783779378037813782378337843785378637873788378937903791379237933794379537963797379837993800380138023803380438053806380738083809381038113812381338143815381638173818381938203821382238233824382538263827382838293830383138323833383438353836383738383839384038413842384338443845384638473848384938503851385238533854385538563857385838593860386138623863386438653866386738683869387038713872387338743875387638773878387938803881388238833884388538863887388838893890389138923893389438953896389738983899390039013902390339043905390639073908390939103911391239133914391539163917391839193920392139223923392439253926392739283929393039313932393339343935393639373938393939403941394239433944394539463947394839493950395139523953395439553956395739583959396039613962396339643965396639673968396939703971397239733974397539763977397839793980398139823983398439853986398739883989399039913992399339943995399639973998399940004001400240034004400540064007400840094010401140124013401440154016401740184019402040214022402340244025402640274028402940304031403240334034403540364037403840394040404140424043404440454046404740484049405040514052405340544055405640574058405940604061406240634064406540664067406840694070407140724073407440754076407740784079408040814082408340844085408640874088408940904091409240934094409540964097409840994100410141024103410441054106410741084109411041114112411341144115411641174118411941204121412241234124412541264127412841294130413141324133413441354136413741384139414041414142414341444145414641474148414941504151415241534154415541564157415841594160416141624163416441654166416741684169417041714172417341744175417641774178417941804181418241834184418541864187418841894190419141924193419441954196419741984199420042014202420342044205420642074208420942104211421242134214421542164217421842194220422142224223422442254226422742284229423042314232423342344235423642374238423942404241424242434244424542464247424842494250425142524253425442554256425742584259426042614262426342644265426642674268426942704271427242734274427542764277427842794280428142824283428442854286428742884289429042914292429342944295429642974298429943004301430243034304430543064307430843094310431143124313431443154316431743184319432043214322432343244325432643274328432943304331433243334334433543364337433843394340434143424343434443454346434743484349435043514352435343544355435643574358435943604361436243634364436543664367436843694370437143724373437443754376437743784379438043814382438343844385438643874388438943904391439243934394439543964397439843994400440144024403440444054406440744084409441044114412441344144415441644174418441944204421442244234424442544264427442844294430443144324433443444354436443744384439444044414442444344444445444644474448444944504451445244534454445544564457445844594460446144624463446444654466446744684469447044714472447344744475447644774478447944804481448244834484448544864487448844894490449144924493449444954496449744984499450045014502450345044505450645074508450945104511451245134514451545164517451845194520452145224523452445254526452745284529453045314532453345344535453645374538453945404541454245434544454545464547454845494550455145524553455445554556455745584559456045614562456345644565456645674568456945704571457245734574457545764577457845794580458145824583458445854586458745884589459045914592459345944595459645974598459946004601460246034604460546064607460846094610461146124613461446154616461746184619462046214622462346244625462646274628462946304631463246334634463546364637463846394640464146424643464446454646464746484649465046514652465346544655465646574658465946604661466246634664466546664667466846694670467146724673467446754676467746784679468046814682468346844685468646874688468946904691469246934694469546964697469846994700470147024703470447054706470747084709471047114712471347144715471647174718471947204721472247234724472547264727472847294730473147324733473447354736473747384739474047414742474347444745474647474748474947504751475247534754475547564757475847594760476147624763476447654766476747684769477047714772477347744775477647774778477947804781478247834784478547864787478847894790479147924793479447954796479747984799480048014802480348044805480648074808480948104811481248134814481548164817481848194820482148224823482448254826482748284829483048314832483348344835483648374838483948404841484248434844484548464847484848494850485148524853485448554856485748584859486048614862486348644865486648674868486948704871487248734874487548764877487848794880488148824883488448854886488748884889489048914892489348944895489648974898489949004901490249034904490549064907490849094910491149124913491449154916491749184919492049214922492349244925492649274928492949304931493249334934493549364937493849394940494149424943494449454946494749484949495049514952495349544955495649574958495949604961496249634964496549664967496849694970497149724973497449754976497749784979498049814982498349844985498649874988498949904991499249934994499549964997499849995000500150025003500450055006500750085009501050115012501350145015501650175018501950205021502250235024502550265027502850295030503150325033503450355036503750385039504050415042504350445045504650475048504950505051505250535054505550565057505850595060506150625063506450655066506750685069507050715072507350745075507650775078507950805081508250835084508550865087508850895090509150925093509450955096509750985099510051015102510351045105510651075108510951105111511251135114511551165117511851195120512151225123512451255126512751285129513051315132513351345135513651375138513951405141514251435144514551465147514851495150515151525153515451555156515751585159516051615162516351645165516651675168516951705171517251735174517551765177517851795180518151825183518451855186518751885189519051915192519351945195519651975198519952005201520252035204520552065207520852095210521152125213521452155216521752185219522052215222522352245225522652275228522952305231523252335234523552365237523852395240524152425243524452455246524752485249525052515252525352545255525652575258525952605261526252635264526552665267526852695270527152725273527452755276527752785279528052815282528352845285528652875288528952905291529252935294529552965297529852995300530153025303530453055306530753085309531053115312531353145315531653175318531953205321532253235324532553265327532853295330533153325333533453355336533753385339534053415342534353445345534653475348534953505351535253535354535553565357535853595360536153625363536453655366536753685369537053715372537353745375537653775378537953805381538253835384538553865387538853895390539153925393539453955396539753985399540054015402540354045405540654075408540954105411541254135414541554165417541854195420542154225423542454255426542754285429543054315432543354345435543654375438543954405441544254435444544554465447544854495450545154525453545454555456545754585459546054615462546354645465546654675468546954705471547254735474547554765477547854795480548154825483548454855486548754885489549054915492549354945495549654975498549955005501550255035504550555065507550855095510551155125513551455155516551755185519552055215522552355245525552655275528552955305531553255335534553555365537553855395540554155425543554455455546554755485549555055515552555355545555555655575558555955605561556255635564556555665567556855695570557155725573557455755576557755785579558055815582558355845585558655875588558955905591559255935594559555965597559855995600560156025603560456055606560756085609561056115612561356145615561656175618561956205621562256235624562556265627562856295630563156325633563456355636563756385639564056415642564356445645564656475648564956505651565256535654565556565657565856595660566156625663566456655666566756685669567056715672567356745675567656775678567956805681568256835684568556865687568856895690569156925693569456955696569756985699570057015702570357045705570657075708570957105711571257135714571557165717571857195720572157225723572457255726572757285729573057315732573357345735573657375738573957405741574257435744574557465747574857495750575157525753575457555756575757585759576057615762576357645765576657675768576957705771577257735774577557765777577857795780578157825783578457855786578757885789579057915792579357945795579657975798579958005801580258035804580558065807580858095810581158125813581458155816581758185819582058215822582358245825582658275828582958305831583258335834583558365837583858395840584158425843584458455846584758485849585058515852585358545855585658575858585958605861586258635864586558665867586858695870587158725873587458755876587758785879588058815882588358845885588658875888588958905891589258935894589558965897589858995900590159025903590459055906590759085909591059115912591359145915591659175918591959205921592259235924592559265927592859295930593159325933593459355936593759385939594059415942594359445945594659475948594959505951595259535954595559565957595859595960596159625963596459655966596759685969597059715972597359745975597659775978597959805981598259835984598559865987598859895990599159925993599459955996599759985999600060016002600360046005600660076008600960106011601260136014601560166017601860196020602160226023602460256026602760286029603060316032603360346035603660376038603960406041604260436044604560466047604860496050605160526053605460556056605760586059606060616062606360646065606660676068606960706071607260736074607560766077607860796080608160826083608460856086608760886089609060916092609360946095609660976098609961006101610261036104610561066107610861096110611161126113611461156116611761186119612061216122612361246125612661276128612961306131613261336134613561366137613861396140614161426143614461456146614761486149615061516152615361546155615661576158615961606161616261636164616561666167616861696170617161726173617461756176617761786179618061816182618361846185618661876188618961906191619261936194619561966197619861996200620162026203620462056206620762086209621062116212621362146215621662176218621962206221622262236224622562266227622862296230623162326233623462356236623762386239624062416242624362446245624662476248624962506251625262536254625562566257625862596260626162626263626462656266626762686269627062716272627362746275627662776278627962806281628262836284628562866287628862896290629162926293629462956296629762986299630063016302630363046305630663076308630963106311631263136314631563166317631863196320632163226323632463256326632763286329633063316332633363346335633663376338633963406341634263436344634563466347634863496350635163526353635463556356635763586359636063616362636363646365636663676368636963706371637263736374637563766377637863796380638163826383638463856386638763886389639063916392639363946395639663976398639964006401640264036404640564066407640864096410641164126413641464156416641764186419642064216422642364246425642664276428642964306431643264336434643564366437643864396440644164426443644464456446644764486449645064516452645364546455645664576458645964606461646264636464646564666467646864696470647164726473647464756476647764786479648064816482648364846485648664876488648964906491649264936494649564966497649864996500650165026503650465056506650765086509651065116512651365146515651665176518651965206521652265236524652565266527652865296530653165326533653465356536653765386539654065416542654365446545654665476548654965506551655265536554655565566557655865596560656165626563656465656566656765686569657065716572657365746575657665776578657965806581658265836584658565866587658865896590659165926593659465956596659765986599660066016602660366046605660666076608660966106611661266136614661566166617661866196620662166226623662466256626662766286629663066316632663366346635663666376638663966406641664266436644664566466647664866496650665166526653665466556656665766586659666066616662666366646665666666676668666966706671667266736674667566766677667866796680668166826683668466856686668766886689669066916692669366946695669666976698669967006701670267036704670567066707670867096710671167126713671467156716671767186719672067216722672367246725672667276728672967306731673267336734673567366737673867396740674167426743674467456746674767486749675067516752675367546755675667576758675967606761676267636764676567666767676867696770677167726773677467756776677767786779678067816782678367846785678667876788678967906791679267936794679567966797679867996800680168026803680468056806680768086809681068116812681368146815681668176818681968206821682268236824682568266827682868296830683168326833683468356836683768386839684068416842684368446845684668476848684968506851685268536854685568566857685868596860686168626863686468656866686768686869687068716872687368746875687668776878687968806881688268836884688568866887688868896890689168926893689468956896689768986899690069016902690369046905690669076908690969106911691269136914691569166917691869196920692169226923692469256926692769286929693069316932693369346935693669376938693969406941694269436944694569466947694869496950695169526953695469556956695769586959696069616962696369646965696669676968696969706971697269736974697569766977697869796980698169826983698469856986698769886989699069916992699369946995699669976998699970007001700270037004700570067007700870097010701170127013701470157016701770187019702070217022702370247025702670277028702970307031703270337034703570367037703870397040704170427043704470457046704770487049705070517052705370547055705670577058705970607061706270637064706570667067706870697070707170727073707470757076707770787079708070817082708370847085708670877088708970907091709270937094709570967097709870997100710171027103710471057106710771087109711071117112711371147115711671177118711971207121712271237124712571267127712871297130713171327133713471357136713771387139714071417142714371447145714671477148714971507151715271537154715571567157715871597160716171627163716471657166716771687169717071717172717371747175717671777178717971807181718271837184718571867187718871897190719171927193719471957196719771987199720072017202720372047205720672077208720972107211721272137214721572167217721872197220722172227223722472257226722772287229723072317232723372347235723672377238723972407241724272437244724572467247724872497250725172527253725472557256725772587259726072617262726372647265726672677268726972707271727272737274727572767277727872797280728172827283728472857286728772887289729072917292729372947295729672977298729973007301730273037304730573067307730873097310731173127313731473157316731773187319732073217322732373247325732673277328732973307331733273337334733573367337733873397340734173427343734473457346734773487349735073517352735373547355735673577358735973607361736273637364736573667367736873697370737173727373737473757376737773787379738073817382738373847385738673877388738973907391739273937394739573967397739873997400740174027403740474057406740774087409741074117412741374147415741674177418741974207421742274237424742574267427742874297430743174327433743474357436743774387439744074417442744374447445744674477448744974507451745274537454745574567457745874597460746174627463746474657466746774687469747074717472747374747475747674777478747974807481748274837484748574867487748874897490749174927493749474957496749774987499750075017502750375047505750675077508750975107511751275137514751575167517751875197520752175227523752475257526752775287529753075317532753375347535753675377538753975407541754275437544754575467547754875497550755175527553755475557556755775587559756075617562756375647565756675677568756975707571757275737574757575767577757875797580758175827583758475857586758775887589759075917592759375947595759675977598759976007601760276037604760576067607760876097610761176127613761476157616761776187619762076217622762376247625762676277628762976307631763276337634763576367637763876397640764176427643764476457646764776487649765076517652765376547655765676577658765976607661766276637664766576667667766876697670767176727673767476757676767776787679768076817682768376847685768676877688768976907691769276937694769576967697769876997700770177027703770477057706770777087709771077117712771377147715771677177718771977207721772277237724772577267727772877297730773177327733773477357736773777387739774077417742774377447745774677477748774977507751775277537754775577567757775877597760776177627763776477657766776777687769777077717772777377747775777677777778777977807781778277837784778577867787778877897790779177927793779477957796779777987799780078017802780378047805780678077808780978107811781278137814781578167817781878197820782178227823782478257826782778287829783078317832783378347835783678377838783978407841784278437844784578467847784878497850785178527853785478557856785778587859786078617862786378647865786678677868786978707871787278737874787578767877787878797880788178827883788478857886788778887889789078917892789378947895789678977898789979007901790279037904790579067907790879097910791179127913791479157916791779187919792079217922792379247925792679277928792979307931793279337934793579367937793879397940794179427943794479457946794779487949795079517952795379547955795679577958795979607961796279637964796579667967796879697970797179727973797479757976797779787979798079817982798379847985798679877988798979907991799279937994799579967997799879998000800180028003800480058006800780088009801080118012801380148015801680178018801980208021802280238024802580268027802880298030803180328033803480358036803780388039804080418042804380448045804680478048804980508051805280538054805580568057805880598060806180628063806480658066806780688069807080718072807380748075807680778078807980808081808280838084808580868087808880898090809180928093809480958096809780988099810081018102810381048105810681078108810981108111811281138114811581168117811881198120812181228123812481258126812781288129813081318132813381348135813681378138813981408141814281438144814581468147814881498150815181528153815481558156815781588159816081618162816381648165816681678168816981708171817281738174817581768177817881798180818181828183818481858186818781888189819081918192819381948195819681978198819982008201820282038204820582068207820882098210821182128213821482158216821782188219822082218222822382248225822682278228822982308231823282338234823582368237823882398240824182428243824482458246824782488249825082518252825382548255825682578258825982608261826282638264826582668267826882698270827182728273827482758276827782788279828082818282828382848285828682878288828982908291829282938294829582968297829882998300830183028303830483058306830783088309831083118312831383148315831683178318831983208321832283238324832583268327832883298330833183328333833483358336833783388339834083418342834383448345834683478348834983508351835283538354835583568357835883598360836183628363836483658366836783688369837083718372837383748375837683778378837983808381838283838384838583868387838883898390839183928393839483958396839783988399840084018402840384048405840684078408840984108411841284138414841584168417841884198420842184228423842484258426842784288429843084318432843384348435843684378438843984408441844284438444844584468447844884498450845184528453845484558456845784588459846084618462846384648465846684678468846984708471847284738474847584768477847884798480848184828483848484858486848784888489849084918492849384948495849684978498849985008501850285038504850585068507850885098510851185128513851485158516851785188519852085218522852385248525852685278528852985308531853285338534853585368537853885398540854185428543854485458546854785488549855085518552855385548555855685578558855985608561856285638564856585668567856885698570857185728573857485758576857785788579858085818582858385848585858685878588858985908591859285938594859585968597859885998600860186028603860486058606860786088609861086118612861386148615861686178618861986208621862286238624862586268627862886298630863186328633863486358636863786388639864086418642864386448645864686478648864986508651865286538654865586568657865886598660866186628663866486658666866786688669867086718672867386748675867686778678867986808681868286838684868586868687868886898690869186928693869486958696869786988699870087018702870387048705870687078708870987108711871287138714871587168717871887198720872187228723872487258726872787288729873087318732873387348735873687378738873987408741874287438744874587468747874887498750875187528753875487558756875787588759876087618762876387648765876687678768876987708771877287738774877587768777877887798780878187828783878487858786878787888789879087918792879387948795879687978798879988008801880288038804880588068807880888098810881188128813881488158816881788188819882088218822882388248825882688278828882988308831883288338834883588368837883888398840884188428843884488458846884788488849885088518852885388548855885688578858885988608861886288638864886588668867886888698870887188728873887488758876887788788879888088818882888388848885888688878888888988908891889288938894889588968897889888998900890189028903890489058906890789088909891089118912891389148915891689178918891989208921892289238924892589268927892889298930893189328933893489358936893789388939894089418942894389448945894689478948894989508951895289538954895589568957895889598960896189628963896489658966896789688969897089718972897389748975897689778978897989808981898289838984898589868987898889898990899189928993899489958996899789988999900090019002900390049005900690079008900990109011901290139014901590169017901890199020902190229023902490259026902790289029903090319032903390349035903690379038903990409041904290439044904590469047904890499050905190529053905490559056905790589059906090619062906390649065906690679068906990709071907290739074907590769077907890799080908190829083908490859086908790889089909090919092909390949095909690979098909991009101910291039104910591069107910891099110911191129113911491159116911791189119912091219122912391249125912691279128912991309131913291339134913591369137913891399140914191429143914491459146914791489149915091519152915391549155915691579158915991609161916291639164916591669167916891699170917191729173917491759176917791789179918091819182918391849185918691879188918991909191919291939194919591969197919891999200920192029203920492059206920792089209921092119212921392149215921692179218921992209221922292239224922592269227922892299230923192329233923492359236923792389239924092419242924392449245924692479248924992509251925292539254925592569257925892599260926192629263926492659266926792689269927092719272927392749275927692779278927992809281928292839284928592869287928892899290929192929293929492959296929792989299930093019302930393049305930693079308930993109311931293139314931593169317931893199320932193229323932493259326932793289329933093319332933393349335933693379338933993409341934293439344934593469347934893499350935193529353935493559356935793589359936093619362936393649365936693679368936993709371937293739374937593769377937893799380938193829383938493859386938793889389939093919392939393949395939693979398939994009401940294039404940594069407940894099410941194129413941494159416941794189419942094219422942394249425942694279428942994309431943294339434943594369437943894399440944194429443944494459446944794489449945094519452945394549455945694579458945994609461946294639464946594669467946894699470947194729473947494759476947794789479948094819482948394849485948694879488948994909491949294939494949594969497949894999500950195029503950495059506950795089509951095119512951395149515951695179518951995209521952295239524952595269527952895299530953195329533953495359536953795389539954095419542954395449545954695479548954995509551955295539554955595569557955895599560956195629563956495659566956795689569957095719572957395749575957695779578957995809581958295839584958595869587958895899590959195929593959495959596959795989599960096019602960396049605960696079608960996109611961296139614961596169617961896199620962196229623962496259626962796289629963096319632963396349635963696379638963996409641964296439644964596469647964896499650965196529653965496559656965796589659966096619662966396649665966696679668966996709671967296739674967596769677967896799680968196829683968496859686968796889689969096919692969396949695969696979698969997009701970297039704970597069707970897099710971197129713971497159716971797189719972097219722972397249725972697279728972997309731973297339734973597369737973897399740974197429743974497459746974797489749975097519752975397549755975697579758975997609761976297639764976597669767976897699770977197729773977497759776977797789779978097819782978397849785978697879788978997909791979297939794979597969797979897999800980198029803980498059806980798089809981098119812981398149815981698179818981998209821982298239824982598269827982898299830983198329833983498359836983798389839984098419842984398449845984698479848984998509851985298539854985598569857985898599860986198629863986498659866986798689869987098719872987398749875987698779878987998809881988298839884988598869887988898899890989198929893989498959896989798989899990099019902990399049905990699079908990999109911991299139914991599169917991899199920992199229923992499259926992799289929993099319932993399349935993699379938993999409941994299439944994599469947994899499950995199529953995499559956995799589959996099619962996399649965996699679968996999709971997299739974997599769977997899799980998199829983998499859986998799889989999099919992999399949995999699979998999910000100011000210003100041000510006100071000810009100101001110012100131001410015100161001710018100191002010021100221002310024100251002610027100281002910030100311003210033100341003510036100371003810039100401004110042100431004410045100461004710048100491005010051100521005310054100551005610057100581005910060100611006210063100641006510066100671006810069100701007110072100731007410075100761007710078100791008010081100821008310084100851008610087100881008910090100911009210093100941009510096100971009810099101001010110102101031010410105101061010710108101091011010111101121011310114101151011610117101181011910120101211012210123101241012510126101271012810129101301013110132101331013410135101361013710138101391014010141101421014310144101451014610147101481014910150101511015210153101541015510156101571015810159101601016110162101631016410165101661016710168101691017010171101721017310174101751017610177101781017910180101811018210183101841018510186101871018810189101901019110192101931019410195101961019710198101991020010201102021020310204102051020610207102081020910210102111021210213102141021510216102171021810219102201022110222102231022410225102261022710228102291023010231102321023310234102351023610237102381023910240102411024210243102441024510246102471024810249102501025110252102531025410255102561025710258102591026010261102621026310264102651026610267102681026910270102711027210273102741027510276102771027810279102801028110282102831028410285102861028710288102891029010291102921029310294102951029610297102981029910300103011030210303103041030510306103071030810309103101031110312103131031410315103161031710318103191032010321103221032310324103251032610327103281032910330103311033210333103341033510336103371033810339103401034110342103431034410345103461034710348103491035010351103521035310354103551035610357103581035910360103611036210363103641036510366103671036810369103701037110372103731037410375103761037710378103791038010381103821038310384103851038610387103881038910390103911039210393103941039510396103971039810399104001040110402104031040410405104061040710408104091041010411104121041310414104151041610417104181041910420104211042210423104241042510426104271042810429104301043110432104331043410435104361043710438104391044010441104421044310444104451044610447104481044910450104511045210453104541045510456104571045810459104601046110462104631046410465104661046710468104691047010471104721047310474104751047610477104781047910480104811048210483104841048510486104871048810489104901049110492104931049410495104961049710498104991050010501105021050310504105051050610507105081050910510105111051210513105141051510516105171051810519105201052110522105231052410525105261052710528105291053010531105321053310534105351053610537105381053910540105411054210543105441054510546105471054810549105501055110552105531055410555105561055710558105591056010561105621056310564105651056610567105681056910570105711057210573105741057510576105771057810579105801058110582105831058410585105861058710588105891059010591105921059310594105951059610597105981059910600106011060210603106041060510606106071060810609106101061110612106131061410615106161061710618106191062010621106221062310624106251062610627106281062910630106311063210633106341063510636106371063810639106401064110642106431064410645106461064710648106491065010651106521065310654106551065610657106581065910660106611066210663106641066510666106671066810669106701067110672106731067410675106761067710678106791068010681106821068310684106851068610687106881068910690106911069210693106941069510696106971069810699107001070110702107031070410705107061070710708107091071010711107121071310714107151071610717107181071910720107211072210723107241072510726107271072810729107301073110732107331073410735107361073710738107391074010741107421074310744107451074610747107481074910750107511075210753107541075510756107571075810759107601076110762107631076410765107661076710768107691077010771107721077310774107751077610777107781077910780107811078210783107841078510786107871078810789107901079110792107931079410795107961079710798107991080010801108021080310804108051080610807108081080910810108111081210813108141081510816108171081810819108201082110822108231082410825108261082710828108291083010831108321083310834108351083610837108381083910840108411084210843108441084510846108471084810849108501085110852108531085410855108561085710858108591086010861108621086310864108651086610867108681086910870108711087210873108741087510876108771087810879108801088110882108831088410885108861088710888108891089010891108921089310894108951089610897108981089910900109011090210903109041090510906109071090810909109101091110912109131091410915109161091710918109191092010921109221092310924109251092610927109281092910930109311093210933109341093510936109371093810939109401094110942109431094410945109461094710948109491095010951109521095310954109551095610957109581095910960109611096210963109641096510966109671096810969109701097110972109731097410975109761097710978109791098010981109821098310984109851098610987109881098910990109911099210993109941099510996109971099810999110001100111002110031100411005110061100711008110091101011011110121101311014110151101611017110181101911020110211102211023110241102511026110271102811029110301103111032110331103411035110361103711038110391104011041110421104311044110451104611047110481104911050110511105211053110541105511056110571105811059110601106111062110631106411065110661106711068110691107011071110721107311074110751107611077110781107911080110811108211083110841108511086110871108811089110901109111092110931109411095110961109711098110991110011101111021110311104111051110611107111081110911110111111111211113111141111511116111171111811119111201112111122111231112411125111261112711128111291113011131111321113311134111351113611137111381113911140111411114211143111441114511146111471114811149111501115111152111531115411155111561115711158111591116011161111621116311164111651116611167111681116911170111711117211173111741117511176111771117811179111801118111182111831118411185111861118711188111891119011191111921119311194111951119611197111981119911200112011120211203112041120511206112071120811209112101121111212112131121411215112161121711218112191122011221112221122311224112251122611227112281122911230112311123211233112341123511236112371123811239112401124111242112431124411245112461124711248112491125011251112521125311254112551125611257112581125911260112611126211263112641126511266112671126811269112701127111272112731127411275112761127711278112791128011281112821128311284112851128611287112881128911290112911129211293112941129511296112971129811299113001130111302113031130411305113061130711308113091131011311113121131311314113151131611317113181131911320113211132211323113241132511326113271132811329113301133111332113331133411335113361133711338113391134011341113421134311344113451134611347113481134911350113511135211353113541135511356113571135811359113601136111362113631136411365113661136711368113691137011371113721137311374113751137611377113781137911380113811138211383113841138511386113871138811389113901139111392113931139411395113961139711398113991140011401114021140311404114051140611407114081140911410114111141211413114141141511416114171141811419114201142111422114231142411425114261142711428114291143011431114321143311434114351143611437114381143911440114411144211443114441144511446114471144811449114501145111452114531145411455114561145711458114591146011461114621146311464114651146611467114681146911470114711147211473114741147511476114771147811479114801148111482114831148411485114861148711488114891149011491114921149311494114951149611497114981149911500115011150211503115041150511506115071150811509115101151111512115131151411515115161151711518115191152011521115221152311524115251152611527115281152911530115311153211533115341153511536115371153811539115401154111542115431154411545115461154711548115491155011551115521155311554115551155611557115581155911560115611156211563115641156511566115671156811569115701157111572115731157411575115761157711578115791158011581115821158311584115851158611587115881158911590115911159211593115941159511596115971159811599116001160111602116031160411605116061160711608116091161011611116121161311614116151161611617116181161911620116211162211623116241162511626116271162811629116301163111632116331163411635116361163711638116391164011641116421164311644116451164611647116481164911650116511165211653116541165511656116571165811659116601166111662116631166411665116661166711668116691167011671116721167311674116751167611677116781167911680116811168211683116841168511686116871168811689116901169111692116931169411695116961169711698116991170011701117021170311704117051170611707117081170911710117111171211713117141171511716117171171811719117201172111722117231172411725117261172711728117291173011731117321173311734117351173611737117381173911740117411174211743117441174511746117471174811749117501175111752117531175411755117561175711758117591176011761117621176311764117651176611767117681176911770117711177211773117741177511776117771177811779117801178111782117831178411785117861178711788117891179011791117921179311794117951179611797117981179911800118011180211803118041180511806118071180811809118101181111812118131181411815118161181711818118191182011821118221182311824118251182611827118281182911830118311183211833118341183511836118371183811839118401184111842118431184411845118461184711848118491185011851118521185311854118551185611857118581185911860118611186211863118641186511866118671186811869118701187111872118731187411875118761187711878118791188011881118821188311884118851188611887118881188911890118911189211893118941189511896118971189811899119001190111902119031190411905119061190711908119091191011911119121191311914119151191611917119181191911920119211192211923119241192511926119271192811929119301193111932119331193411935119361193711938119391194011941119421194311944119451194611947119481194911950119511195211953119541195511956119571195811959119601196111962119631196411965119661196711968119691197011971119721197311974119751197611977119781197911980119811198211983119841198511986119871198811989119901199111992119931199411995119961199711998119991200012001120021200312004120051200612007120081200912010120111201212013120141201512016120171201812019120201202112022120231202412025120261202712028120291203012031120321203312034120351203612037120381203912040120411204212043120441204512046120471204812049120501205112052120531205412055120561205712058120591206012061120621206312064120651206612067120681206912070120711207212073120741207512076120771207812079120801208112082120831208412085120861208712088120891209012091120921209312094120951209612097120981209912100121011210212103121041210512106121071210812109121101211112112121131211412115121161211712118121191212012121121221212312124121251212612127121281212912130121311213212133121341213512136121371213812139121401214112142121431214412145121461214712148121491215012151121521215312154121551215612157121581215912160121611216212163121641216512166121671216812169121701217112172121731217412175121761217712178121791218012181121821218312184121851218612187121881218912190121911219212193121941219512196121971219812199122001220112202122031220412205122061220712208122091221012211122121221312214122151221612217122181221912220122211222212223122241222512226122271222812229122301223112232122331223412235122361223712238122391224012241122421224312244122451224612247122481224912250122511225212253122541225512256122571225812259122601226112262122631226412265122661226712268122691227012271122721227312274122751227612277122781227912280122811228212283122841228512286122871228812289122901229112292122931229412295122961229712298122991230012301123021230312304123051230612307123081230912310123111231212313123141231512316123171231812319123201232112322123231232412325123261232712328123291233012331123321233312334123351233612337123381233912340123411234212343123441234512346123471234812349123501235112352123531235412355123561235712358123591236012361123621236312364123651236612367123681236912370123711237212373123741237512376123771237812379123801238112382123831238412385123861238712388123891239012391123921239312394123951239612397123981239912400124011240212403124041240512406124071240812409124101241112412124131241412415124161241712418124191242012421124221242312424124251242612427124281242912430124311243212433124341243512436124371243812439124401244112442124431244412445124461244712448124491245012451124521245312454124551245612457124581245912460124611246212463124641246512466124671246812469124701247112472124731247412475124761247712478124791248012481124821248312484124851248612487124881248912490124911249212493124941249512496124971249812499125001250112502125031250412505125061250712508125091251012511125121251312514125151251612517125181251912520125211252212523125241252512526125271252812529125301253112532125331253412535125361253712538125391254012541125421254312544125451254612547125481254912550125511255212553125541255512556125571255812559125601256112562125631256412565125661256712568125691257012571125721257312574125751257612577125781257912580125811258212583125841258512586125871258812589125901259112592125931259412595125961259712598125991260012601126021260312604126051260612607126081260912610126111261212613126141261512616126171261812619126201262112622126231262412625126261262712628126291263012631126321263312634126351263612637126381263912640126411264212643126441264512646126471264812649126501265112652126531265412655126561265712658
  1. #if defined(SOKOL_IMPL) && !defined(SOKOL_APP_IMPL)
  2. #define SOKOL_APP_IMPL
  3. #endif
  4. #ifndef SOKOL_APP_INCLUDED
  5. /*
  6. sokol_app.h -- cross-platform application wrapper
  7. Project URL: https://github.com/floooh/sokol
  8. Do this:
  9. #define SOKOL_IMPL or
  10. #define SOKOL_APP_IMPL
  11. before you include this file in *one* C or C++ file to create the
  12. implementation.
  13. In the same place define one of the following to select the 3D-API
  14. which should be initialized by sokol_app.h (this must also match
  15. the backend selected for sokol_gfx.h if both are used in the same
  16. project):
  17. #define SOKOL_GLCORE33
  18. #define SOKOL_GLES2
  19. #define SOKOL_GLES3
  20. #define SOKOL_D3D11
  21. #define SOKOL_METAL
  22. #define SOKOL_WGPU
  23. Optionally provide the following defines with your own implementations:
  24. SOKOL_ASSERT(c) - your own assert macro (default: assert(c))
  25. SOKOL_LOG(msg) - your own logging function (default: puts(msg))
  26. SOKOL_UNREACHABLE() - a guard macro for unreachable code (default: assert(false))
  27. SOKOL_ABORT() - called after an unrecoverable error (default: abort())
  28. SOKOL_WIN32_FORCE_MAIN - define this on Win32 to use a main() entry point instead of WinMain
  29. SOKOL_NO_ENTRY - define this if sokol_app.h shouldn't "hijack" the main() function
  30. SOKOL_APP_API_DECL - public function declaration prefix (default: extern)
  31. SOKOL_API_DECL - same as SOKOL_APP_API_DECL
  32. SOKOL_API_IMPL - public function implementation prefix (default: -)
  33. SOKOL_CALLOC - your own calloc function (default: calloc(n, s))
  34. SOKOL_FREE - your own free function (default: free(p))
  35. Optionally define the following to force debug checks and validations
  36. even in release mode:
  37. SOKOL_DEBUG - by default this is defined if _DEBUG is defined
  38. If sokol_app.h is compiled as a DLL, define the following before
  39. including the declaration or implementation:
  40. SOKOL_DLL
  41. On Windows, SOKOL_DLL will define SOKOL_APP_API_DECL as __declspec(dllexport)
  42. or __declspec(dllimport) as needed.
  43. For example code, see https://github.com/floooh/sokol-samples/tree/master/sapp
  44. Portions of the Windows and Linux GL initialization, event-, icon- etc... code
  45. have been taken from GLFW (http://www.glfw.org/)
  46. iOS onscreen keyboard support 'inspired' by libgdx.
  47. Link with the following system libraries:
  48. - on macOS with Metal: Cocoa, QuartzCore, Metal, MetalKit
  49. - on macOS with GL: Cocoa, QuartzCore, OpenGL
  50. - on iOS with Metal: Foundation, UIKit, Metal, MetalKit
  51. - on iOS with GL: Foundation, UIKit, OpenGLES, GLKit
  52. - on Linux: X11, Xi, Xcursor, GL, dl, pthread, m(?)
  53. - on Android: GLESv3, EGL, log, android
  54. - on Windows with the MSVC or Clang toolchains: no action needed, libs are defined in-source via pragma-comment-lib
  55. - on Windows with MINGW/MSYS2 gcc: compile with '-mwin32' so that _WIN32 is defined
  56. - link with the following libs: -lkernel32 -luser32 -lshell32
  57. - additionally with the GL backend: -lgdi32
  58. - additionally with the D3D11 backend: -ld3d11 -ldxgi -dxguid
  59. On Linux, you also need to use the -pthread compiler and linker option, otherwise weird
  60. things will happen, see here for details: https://github.com/floooh/sokol/issues/376
  61. Building for UWP requires a recent Visual Studio toolchain and Windows SDK
  62. (at least VS2019 and Windows SDK 10.0.19041.0). When the UWP backend is
  63. selected, the sokol_app.h implementation must be compiled as C++17.
  64. On macOS and iOS, the implementation must be compiled as Objective-C.
  65. FEATURE OVERVIEW
  66. ================
  67. sokol_app.h provides a minimalistic cross-platform API which
  68. implements the 'application-wrapper' parts of a 3D application:
  69. - a common application entry function
  70. - creates a window and 3D-API context/device with a 'default framebuffer'
  71. - makes the rendered frame visible
  72. - provides keyboard-, mouse- and low-level touch-events
  73. - platforms: MacOS, iOS, HTML5, Win32, Linux, Android (TODO: RaspberryPi)
  74. - 3D-APIs: Metal, D3D11, GL3.2, GLES2, GLES3, WebGL, WebGL2
  75. FEATURE/PLATFORM MATRIX
  76. =======================
  77. | Windows | macOS | Linux | iOS | Android | UWP | Raspi | HTML5
  78. --------------------+---------+-------+-------+-------+---------+------+-------+-------
  79. gl 3.x | YES | YES | YES | --- | --- | --- | --- | ---
  80. gles2/webgl | --- | --- | --- | YES | YES | --- | TODO | YES
  81. gles3/webgl2 | --- | --- | --- | YES | YES | --- | --- | YES
  82. metal | --- | YES | --- | YES | --- | --- | --- | ---
  83. d3d11 | YES | --- | --- | --- | --- | YES | --- | ---
  84. KEY_DOWN | YES | YES | YES | SOME | TODO | YES | TODO | YES
  85. KEY_UP | YES | YES | YES | SOME | TODO | YES | TODO | YES
  86. CHAR | YES | YES | YES | YES | TODO | YES | TODO | YES
  87. MOUSE_DOWN | YES | YES | YES | --- | --- | YES | TODO | YES
  88. MOUSE_UP | YES | YES | YES | --- | --- | YES | TODO | YES
  89. MOUSE_SCROLL | YES | YES | YES | --- | --- | YES | TODO | YES
  90. MOUSE_MOVE | YES | YES | YES | --- | --- | YES | TODO | YES
  91. MOUSE_ENTER | YES | YES | YES | --- | --- | YES | TODO | YES
  92. MOUSE_LEAVE | YES | YES | YES | --- | --- | YES | TODO | YES
  93. TOUCHES_BEGAN | --- | --- | --- | YES | YES | TODO | --- | YES
  94. TOUCHES_MOVED | --- | --- | --- | YES | YES | TODO | --- | YES
  95. TOUCHES_ENDED | --- | --- | --- | YES | YES | TODO | --- | YES
  96. TOUCHES_CANCELLED | --- | --- | --- | YES | YES | TODO | --- | YES
  97. RESIZED | YES | YES | YES | YES | YES | YES | --- | YES
  98. ICONIFIED | YES | YES | YES | --- | --- | YES | --- | ---
  99. RESTORED | YES | YES | YES | --- | --- | YES | --- | ---
  100. SUSPENDED | --- | --- | --- | YES | YES | YES | --- | TODO
  101. RESUMED | --- | --- | --- | YES | YES | YES | --- | TODO
  102. QUIT_REQUESTED | YES | YES | YES | --- | --- | --- | TODO | YES
  103. UPDATE_CURSOR | YES | YES | TODO | --- | --- | TODO | --- | TODO
  104. IME | TODO | TODO? | TODO | ??? | TODO | --- | ??? | ???
  105. key repeat flag | YES | YES | YES | --- | --- | YES | TODO | YES
  106. windowed | YES | YES | YES | --- | --- | YES | TODO | YES
  107. fullscreen | YES | YES | YES | YES | YES | YES | TODO | ---
  108. mouse hide | YES | YES | YES | --- | --- | YES | TODO | TODO
  109. mouse lock | YES | YES | YES | --- | --- | TODO | TODO | YES
  110. screen keyboard | --- | --- | --- | YES | TODO | TODO | --- | YES
  111. swap interval | YES | YES | YES | YES | TODO | --- | TODO | YES
  112. high-dpi | YES | YES | TODO | YES | YES | YES | TODO | YES
  113. clipboard | YES | YES | TODO | --- | --- | TODO | --- | YES
  114. MSAA | YES | YES | YES | YES | YES | TODO | TODO | YES
  115. drag'n'drop | YES | YES | YES | --- | --- | TODO | TODO | YES
  116. window icon | YES | YES(1)| YES | --- | --- | TODO | TODO | YES
  117. (1) macOS has no regular window icons, instead the dock icon is changed
  118. STEP BY STEP
  119. ============
  120. --- Add a sokol_main() function to your code which returns a sapp_desc structure
  121. with initialization parameters and callback function pointers. This
  122. function is called very early, usually at the start of the
  123. platform's entry function (e.g. main or WinMain). You should do as
  124. little as possible here, since the rest of your code might be called
  125. from another thread (this depends on the platform):
  126. sapp_desc sokol_main(int argc, char* argv[]) {
  127. return (sapp_desc) {
  128. .width = 640,
  129. .height = 480,
  130. .init_cb = my_init_func,
  131. .frame_cb = my_frame_func,
  132. .cleanup_cb = my_cleanup_func,
  133. .event_cb = my_event_func,
  134. ...
  135. };
  136. }
  137. There are many more setup parameters, but these are the most important.
  138. For a complete list search for the sapp_desc structure declaration
  139. below.
  140. DO NOT call any sokol-app function from inside sokol_main(), since
  141. sokol-app will not be initialized at this point.
  142. The .width and .height parameters are the preferred size of the 3D
  143. rendering canvas. The actual size may differ from this depending on
  144. platform and other circumstances. Also the canvas size may change at
  145. any time (for instance when the user resizes the application window,
  146. or rotates the mobile device).
  147. All provided function callbacks will be called from the same thread,
  148. but this may be different from the thread where sokol_main() was called.
  149. .init_cb (void (*)(void))
  150. This function is called once after the application window,
  151. 3D rendering context and swap chain have been created. The
  152. function takes no arguments and has no return value.
  153. .frame_cb (void (*)(void))
  154. This is the per-frame callback, which is usually called 60
  155. times per second. This is where your application would update
  156. most of its state and perform all rendering.
  157. .cleanup_cb (void (*)(void))
  158. The cleanup callback is called once right before the application
  159. quits.
  160. .event_cb (void (*)(const sapp_event* event))
  161. The event callback is mainly for input handling, but is also
  162. used to communicate other types of events to the application. Keep the
  163. event_cb struct member zero-initialized if your application doesn't require
  164. event handling.
  165. .fail_cb (void (*)(const char* msg))
  166. The fail callback is called when a fatal error is encountered
  167. during start which doesn't allow the program to continue.
  168. Providing a callback here gives you a chance to show an error message
  169. to the user. The default behaviour is SOKOL_LOG(msg)
  170. As you can see, those 'standard callbacks' don't have a user_data
  171. argument, so any data that needs to be preserved between callbacks
  172. must live in global variables. If keeping state in global variables
  173. is not an option, there's an alternative set of callbacks with
  174. an additional user_data pointer argument:
  175. .user_data (void*)
  176. The user-data argument for the callbacks below
  177. .init_userdata_cb (void (*)(void* user_data))
  178. .frame_userdata_cb (void (*)(void* user_data))
  179. .cleanup_userdata_cb (void (*)(void* user_data))
  180. .event_cb (void(*)(const sapp_event* event, void* user_data))
  181. .fail_cb (void(*)(const char* msg, void* user_data))
  182. These are the user-data versions of the callback functions. You
  183. can mix those with the standard callbacks that don't have the
  184. user_data argument.
  185. The function sapp_userdata() can be used to query the user_data
  186. pointer provided in the sapp_desc struct.
  187. You can also call sapp_query_desc() to get a copy of the
  188. original sapp_desc structure.
  189. NOTE that there's also an alternative compile mode where sokol_app.h
  190. doesn't "hijack" the main() function. Search below for SOKOL_NO_ENTRY.
  191. --- Implement the initialization callback function (init_cb), this is called
  192. once after the rendering surface, 3D API and swap chain have been
  193. initialized by sokol_app. All sokol-app functions can be called
  194. from inside the initialization callback, the most useful functions
  195. at this point are:
  196. int sapp_width(void)
  197. int sapp_height(void)
  198. Returns the current width and height of the default framebuffer in pixels,
  199. this may change from one frame to the next, and it may be different
  200. from the initial size provided in the sapp_desc struct.
  201. float sapp_widthf(void)
  202. float sapp_heightf(void)
  203. These are alternatives to sapp_width() and sapp_height() which return
  204. the default framebuffer size as float values instead of integer. This
  205. may help to prevent casting back and forth between int and float
  206. in more strongly typed languages than C and C++.
  207. int sapp_color_format(void)
  208. int sapp_depth_format(void)
  209. The color and depth-stencil pixelformats of the default framebuffer,
  210. as integer values which are compatible with sokol-gfx's
  211. sg_pixel_format enum (so that they can be plugged directly in places
  212. where sg_pixel_format is expected). Possible values are:
  213. 23 == SG_PIXELFORMAT_RGBA8
  214. 27 == SG_PIXELFORMAT_BGRA8
  215. 41 == SG_PIXELFORMAT_DEPTH
  216. 42 == SG_PIXELFORMAT_DEPTH_STENCIL
  217. int sapp_sample_count(void)
  218. Return the MSAA sample count of the default framebuffer.
  219. bool sapp_gles2(void)
  220. Returns true if a GLES2 or WebGL context has been created. This
  221. is useful when a GLES3/WebGL2 context was requested but is not
  222. available so that sokol_app.h had to fallback to GLES2/WebGL.
  223. const void* sapp_metal_get_device(void)
  224. const void* sapp_metal_get_renderpass_descriptor(void)
  225. const void* sapp_metal_get_drawable(void)
  226. If the Metal backend has been selected, these functions return pointers
  227. to various Metal API objects required for rendering, otherwise
  228. they return a null pointer. These void pointers are actually
  229. Objective-C ids converted with a (ARC) __bridge cast so that
  230. the ids can be tunnel through C code. Also note that the returned
  231. pointers to the renderpass-descriptor and drawable may change from one
  232. frame to the next, only the Metal device object is guaranteed to
  233. stay the same.
  234. const void* sapp_macos_get_window(void)
  235. On macOS, get the NSWindow object pointer, otherwise a null pointer.
  236. Before being used as Objective-C object, the void* must be converted
  237. back with a (ARC) __bridge cast.
  238. const void* sapp_ios_get_window(void)
  239. On iOS, get the UIWindow object pointer, otherwise a null pointer.
  240. Before being used as Objective-C object, the void* must be converted
  241. back with a (ARC) __bridge cast.
  242. const void* sapp_win32_get_hwnd(void)
  243. On Windows, get the window's HWND, otherwise a null pointer. The
  244. HWND has been cast to a void pointer in order to be tunneled
  245. through code which doesn't include Windows.h.
  246. const void* sapp_d3d11_get_device(void)
  247. const void* sapp_d3d11_get_device_context(void)
  248. const void* sapp_d3d11_get_render_target_view(void)
  249. const void* sapp_d3d11_get_depth_stencil_view(void)
  250. Similar to the sapp_metal_* functions, the sapp_d3d11_* functions
  251. return pointers to D3D11 API objects required for rendering,
  252. only if the D3D11 backend has been selected. Otherwise they
  253. return a null pointer. Note that the returned pointers to the
  254. render-target-view and depth-stencil-view may change from one
  255. frame to the next!
  256. const void* sapp_wgpu_get_device(void)
  257. const void* sapp_wgpu_get_render_view(void)
  258. const void* sapp_wgpu_get_resolve_view(void)
  259. const void* sapp_wgpu_get_depth_stencil_view(void)
  260. These are the WebGPU-specific functions to get the WebGPU
  261. objects and values required for rendering. If sokol_app.h
  262. is not compiled with SOKOL_WGPU, these functions return null.
  263. const void* sapp_android_get_native_activity(void);
  264. On Android, get the native activity ANativeActivity pointer, otherwise
  265. a null pointer.
  266. --- Implement the frame-callback function, this function will be called
  267. on the same thread as the init callback, but might be on a different
  268. thread than the sokol_main() function. Note that the size of
  269. the rendering framebuffer might have changed since the frame callback
  270. was called last. Call the functions sapp_width() and sapp_height()
  271. each frame to get the current size.
  272. --- Optionally implement the event-callback to handle input events.
  273. sokol-app provides the following type of input events:
  274. - a 'virtual key' was pressed down or released
  275. - a single text character was entered (provided as UTF-32 code point)
  276. - a mouse button was pressed down or released (left, right, middle)
  277. - mouse-wheel or 2D scrolling events
  278. - the mouse was moved
  279. - the mouse has entered or left the application window boundaries
  280. - low-level, portable multi-touch events (began, moved, ended, cancelled)
  281. - the application window was resized, iconified or restored
  282. - the application was suspended or restored (on mobile platforms)
  283. - the user or application code has asked to quit the application
  284. - a string was pasted to the system clipboard
  285. - one or more files have been dropped onto the application window
  286. To explicitly 'consume' an event and prevent that the event is
  287. forwarded for further handling to the operating system, call
  288. sapp_consume_event() from inside the event handler (NOTE that
  289. this behaviour is currently only implemented for some HTML5
  290. events, support for other platforms and event types will
  291. be added as needed, please open a github ticket and/or provide
  292. a PR if needed).
  293. NOTE: Do *not* call any 3D API rendering functions in the event
  294. callback function, since the 3D API context may not be active when the
  295. event callback is called (it may work on some platforms and 3D APIs,
  296. but not others, and the exact behaviour may change between
  297. sokol-app versions).
  298. --- Implement the cleanup-callback function, this is called once
  299. after the user quits the application (see the section
  300. "APPLICATION QUIT" for detailed information on quitting
  301. behaviour, and how to intercept a pending quit - for instance to show a
  302. "Really Quit?" dialog box). Note that the cleanup-callback isn't
  303. guaranteed to be called on the web and mobile platforms.
  304. MOUSE LOCK (AKA POINTER LOCK, AKA MOUSE CAPTURE)
  305. ================================================
  306. In normal mouse mode, no mouse movement events are reported when the
  307. mouse leaves the windows client area or hits the screen border (whether
  308. it's one or the other depends on the platform), and the mouse move events
  309. (SAPP_EVENTTYPE_MOUSE_MOVE) contain absolute mouse positions in
  310. framebuffer pixels in the sapp_event items mouse_x and mouse_y, and
  311. relative movement in framebuffer pixels in the sapp_event items mouse_dx
  312. and mouse_dy.
  313. To get continuous mouse movement (also when the mouse leaves the window
  314. client area or hits the screen border), activate mouse-lock mode
  315. by calling:
  316. sapp_lock_mouse(true)
  317. When mouse lock is activated, the mouse pointer is hidden, the
  318. reported absolute mouse position (sapp_event.mouse_x/y) appears
  319. frozen, and the relative mouse movement in sapp_event.mouse_dx/dy
  320. no longer has a direct relation to framebuffer pixels but instead
  321. uses "raw mouse input" (what "raw mouse input" exactly means also
  322. differs by platform).
  323. To deactivate mouse lock and return to normal mouse mode, call
  324. sapp_lock_mouse(false)
  325. And finally, to check if mouse lock is currently active, call
  326. if (sapp_mouse_locked()) { ... }
  327. On native platforms, the sapp_lock_mouse() and sapp_mouse_locked()
  328. functions work as expected (mouse lock is activated or deactivated
  329. immediately when sapp_lock_mouse() is called, and sapp_mouse_locked()
  330. also immediately returns the new state after sapp_lock_mouse()
  331. is called.
  332. On the web platform, sapp_lock_mouse() and sapp_mouse_locked() behave
  333. differently, as dictated by the limitations of the HTML5 Pointer Lock API:
  334. - sapp_lock_mouse(true) can be called at any time, but it will
  335. only take effect in a 'short-lived input event handler of a specific
  336. type', meaning when one of the following events happens:
  337. - SAPP_EVENTTYPE_MOUSE_DOWN
  338. - SAPP_EVENTTYPE_MOUSE_UP
  339. - SAPP_EVENTTYPE_MOUSE_SCROLL
  340. - SAPP_EVENTYTPE_KEY_UP
  341. - SAPP_EVENTTYPE_KEY_DOWN
  342. - The mouse lock/unlock action on the web platform is asynchronous,
  343. this means that sapp_mouse_locked() won't immediately return
  344. the new status after calling sapp_lock_mouse(), instead the
  345. reported status will only change when the pointer lock has actually
  346. been activated or deactivated in the browser.
  347. - On the web, mouse lock can be deactivated by the user at any time
  348. by pressing the Esc key. When this happens, sokol_app.h behaves
  349. the same as if sapp_lock_mouse(false) is called.
  350. For things like camera manipulation it's most straightforward to lock
  351. and unlock the mouse right from the sokol_app.h event handler, for
  352. instance the following code enters and leaves mouse lock when the
  353. left mouse button is pressed and released, and then uses the relative
  354. movement information to manipulate a camera (taken from the
  355. cgltf-sapp.c sample in the sokol-samples repository
  356. at https://github.com/floooh/sokol-samples):
  357. static void input(const sapp_event* ev) {
  358. switch (ev->type) {
  359. case SAPP_EVENTTYPE_MOUSE_DOWN:
  360. if (ev->mouse_button == SAPP_MOUSEBUTTON_LEFT) {
  361. sapp_lock_mouse(true);
  362. }
  363. break;
  364. case SAPP_EVENTTYPE_MOUSE_UP:
  365. if (ev->mouse_button == SAPP_MOUSEBUTTON_LEFT) {
  366. sapp_lock_mouse(false);
  367. }
  368. break;
  369. case SAPP_EVENTTYPE_MOUSE_MOVE:
  370. if (sapp_mouse_locked()) {
  371. cam_orbit(&state.camera, ev->mouse_dx * 0.25f, ev->mouse_dy * 0.25f);
  372. }
  373. break;
  374. default:
  375. break;
  376. }
  377. }
  378. CLIPBOARD SUPPORT
  379. =================
  380. Applications can send and receive UTF-8 encoded text data from and to the
  381. system clipboard. By default, clipboard support is disabled and
  382. must be enabled at startup via the following sapp_desc struct
  383. members:
  384. sapp_desc.enable_clipboard - set to true to enable clipboard support
  385. sapp_desc.clipboard_size - size of the internal clipboard buffer in bytes
  386. Enabling the clipboard will dynamically allocate a clipboard buffer
  387. for UTF-8 encoded text data of the requested size in bytes, the default
  388. size is 8 KBytes. Strings that don't fit into the clipboard buffer
  389. (including the terminating zero) will be silently clipped, so it's
  390. important that you provide a big enough clipboard size for your
  391. use case.
  392. To send data to the clipboard, call sapp_set_clipboard_string() with
  393. a pointer to an UTF-8 encoded, null-terminated C-string.
  394. NOTE that on the HTML5 platform, sapp_set_clipboard_string() must be
  395. called from inside a 'short-lived event handler', and there are a few
  396. other HTML5-specific caveats to workaround. You'll basically have to
  397. tinker until it works in all browsers :/ (maybe the situation will
  398. improve when all browsers agree on and implement the new
  399. HTML5 navigator.clipboard API).
  400. To get data from the clipboard, check for the SAPP_EVENTTYPE_CLIPBOARD_PASTED
  401. event in your event handler function, and then call sapp_get_clipboard_string()
  402. to obtain the pasted UTF-8 encoded text.
  403. NOTE that behaviour of sapp_get_clipboard_string() is slightly different
  404. depending on platform:
  405. - on the HTML5 platform, the internal clipboard buffer will only be updated
  406. right before the SAPP_EVENTTYPE_CLIPBOARD_PASTED event is sent,
  407. and sapp_get_clipboard_string() will simply return the current content
  408. of the clipboard buffer
  409. - on 'native' platforms, the call to sapp_get_clipboard_string() will
  410. update the internal clipboard buffer with the most recent data
  411. from the system clipboard
  412. Portable code should check for the SAPP_EVENTTYPE_CLIPBOARD_PASTED event,
  413. and then call sapp_get_clipboard_string() right in the event handler.
  414. The SAPP_EVENTTYPE_CLIPBOARD_PASTED event will be generated by sokol-app
  415. as follows:
  416. - on macOS: when the Cmd+V key is pressed down
  417. - on HTML5: when the browser sends a 'paste' event to the global 'window' object
  418. - on all other platforms: when the Ctrl+V key is pressed down
  419. DRAG AND DROP SUPPORT
  420. =====================
  421. PLEASE NOTE: the drag'n'drop feature works differently on WASM/HTML5
  422. and on the native desktop platforms (Win32, Linux and macOS) because
  423. of security-related restrictions in the HTML5 drag'n'drop API. The
  424. WASM/HTML5 specifics are described at the end of this documentation
  425. section:
  426. Like clipboard support, drag'n'drop support must be explicitly enabled
  427. at startup in the sapp_desc struct.
  428. sapp_desc sokol_main() {
  429. return (sapp_desc) {
  430. .enable_dragndrop = true, // default is false
  431. ...
  432. };
  433. }
  434. You can also adjust the maximum number of files that are accepted
  435. in a drop operation, and the maximum path length in bytes if needed:
  436. sapp_desc sokol_main() {
  437. return (sapp_desc) {
  438. .enable_dragndrop = true, // default is false
  439. .max_dropped_files = 8, // default is 1
  440. .max_dropped_file_path_length = 8192, // in bytes, default is 2048
  441. ...
  442. };
  443. }
  444. When drag'n'drop is enabled, the event callback will be invoked with an
  445. event of type SAPP_EVENTTYPE_FILES_DROPPED whenever the user drops files on
  446. the application window.
  447. After the SAPP_EVENTTYPE_FILES_DROPPED is received, you can query the
  448. number of dropped files, and their absolute paths by calling separate
  449. functions:
  450. void on_event(const sapp_event* ev) {
  451. if (ev->type == SAPP_EVENTTYPE_FILES_DROPPED) {
  452. // the mouse position where the drop happened
  453. float x = ev->mouse_x;
  454. float y = ev->mouse_y;
  455. // get the number of files and their paths like this:
  456. const int num_dropped_files = sapp_get_num_dropped_files();
  457. for (int i = 0; i < num_dropped_files; i++) {
  458. const char* path = sapp_get_dropped_file_path(i);
  459. ...
  460. }
  461. }
  462. }
  463. The returned file paths are UTF-8 encoded strings.
  464. You can call sapp_get_num_dropped_files() and sapp_get_dropped_file_path()
  465. anywhere, also outside the event handler callback, but be aware that the
  466. file path strings will be overwritten with the next drop operation.
  467. In any case, sapp_get_dropped_file_path() will never return a null pointer,
  468. instead an empty string "" will be returned if the drag'n'drop feature
  469. hasn't been enabled, the last drop-operation failed, or the file path index
  470. is out of range.
  471. Drag'n'drop caveats:
  472. - if more files are dropped in a single drop-action
  473. than sapp_desc.max_dropped_files, the additional
  474. files will be silently ignored
  475. - if any of the file paths is longer than
  476. sapp_desc.max_dropped_file_path_length (in number of bytes, after UTF-8
  477. encoding) the entire drop operation will be silently ignored (this
  478. needs some sort of error feedback in the future)
  479. - no mouse positions are reported while the drag is in
  480. process, this may change in the future
  481. Drag'n'drop on HTML5/WASM:
  482. The HTML5 drag'n'drop API doesn't return file paths, but instead
  483. black-box 'file objects' which must be used to load the content
  484. of dropped files. This is the reason why sokol_app.h adds two
  485. HTML5-specific functions to the drag'n'drop API:
  486. uint32_t sapp_html5_get_dropped_file_size(int index)
  487. Returns the size in bytes of a dropped file.
  488. void sapp_html5_fetch_dropped_file(const sapp_html5_fetch_request* request)
  489. Asynchronously loads the content of a dropped file into a
  490. provided memory buffer (which must be big enough to hold
  491. the file content)
  492. To start loading the first dropped file after an SAPP_EVENTTYPE_FILES_DROPPED
  493. event is received:
  494. sapp_html5_fetch_dropped_file(&(sapp_html5_fetch_request){
  495. .dropped_file_index = 0,
  496. .callback = fetch_cb
  497. .buffer_ptr = buf,
  498. .buffer_size = buf_size,
  499. .user_data = ...
  500. });
  501. Make sure that the memory pointed to by 'buf' stays valid until the
  502. callback function is called!
  503. As result of the asynchronous loading operation (no matter if succeeded or
  504. failed) the 'fetch_cb' function will be called:
  505. void fetch_cb(const sapp_html5_fetch_response* response) {
  506. // IMPORTANT: check if the loading operation actually succeeded:
  507. if (response->succeeded) {
  508. // the size of the loaded file:
  509. const uint32_t num_bytes = response->fetched_size;
  510. // and the pointer to the data (same as 'buf' in the fetch-call):
  511. const void* ptr = response->buffer_ptr;
  512. }
  513. else {
  514. // on error check the error code:
  515. switch (response->error_code) {
  516. case SAPP_HTML5_FETCH_ERROR_BUFFER_TOO_SMALL:
  517. ...
  518. break;
  519. case SAPP_HTML5_FETCH_ERROR_OTHER:
  520. ...
  521. break;
  522. }
  523. }
  524. }
  525. Check the droptest-sapp example for a real-world example which works
  526. both on native platforms and the web:
  527. https://github.com/floooh/sokol-samples/blob/master/sapp/droptest-sapp.c
  528. HIGH-DPI RENDERING
  529. ==================
  530. You can set the sapp_desc.high_dpi flag during initialization to request
  531. a full-resolution framebuffer on HighDPI displays. The default behaviour
  532. is sapp_desc.high_dpi=false, this means that the application will
  533. render to a lower-resolution framebuffer on HighDPI displays and the
  534. rendered content will be upscaled by the window system composer.
  535. In a HighDPI scenario, you still request the same window size during
  536. sokol_main(), but the framebuffer sizes returned by sapp_width()
  537. and sapp_height() will be scaled up according to the DPI scaling
  538. ratio. You can also get a DPI scaling factor with the function
  539. sapp_dpi_scale().
  540. Here's an example on a Mac with Retina display:
  541. sapp_desc sokol_main() {
  542. return (sapp_desc) {
  543. .width = 640,
  544. .height = 480,
  545. .high_dpi = true,
  546. ...
  547. };
  548. }
  549. The functions sapp_width(), sapp_height() and sapp_dpi_scale() will
  550. return the following values:
  551. sapp_width -> 1280
  552. sapp_height -> 960
  553. sapp_dpi_scale -> 2.0
  554. If the high_dpi flag is false, or you're not running on a Retina display,
  555. the values would be:
  556. sapp_width -> 640
  557. sapp_height -> 480
  558. sapp_dpi_scale -> 1.0
  559. APPLICATION QUIT
  560. ================
  561. Without special quit handling, a sokol_app.h application will quit
  562. 'gracefully' when the user clicks the window close-button unless a
  563. platform's application model prevents this (e.g. on web or mobile).
  564. 'Graceful exit' means that the application-provided cleanup callback will
  565. be called before the application quits.
  566. On native desktop platforms sokol_app.h provides more control over the
  567. application-quit-process. It's possible to initiate a 'programmatic quit'
  568. from the application code, and a quit initiated by the application user can
  569. be intercepted (for instance to show a custom dialog box).
  570. This 'programmatic quit protocol' is implemented through 3 functions
  571. and 1 event:
  572. - sapp_quit(): This function simply quits the application without
  573. giving the user a chance to intervene. Usually this might
  574. be called when the user clicks the 'Ok' button in a 'Really Quit?'
  575. dialog box
  576. - sapp_request_quit(): Calling sapp_request_quit() will send the
  577. event SAPP_EVENTTYPE_QUIT_REQUESTED to the applications event handler
  578. callback, giving the user code a chance to intervene and cancel the
  579. pending quit process (for instance to show a 'Really Quit?' dialog
  580. box). If the event handler callback does nothing, the application
  581. will be quit as usual. To prevent this, call the function
  582. sapp_cancel_quit() from inside the event handler.
  583. - sapp_cancel_quit(): Cancels a pending quit request, either initiated
  584. by the user clicking the window close button, or programmatically
  585. by calling sapp_request_quit(). The only place where calling this
  586. function makes sense is from inside the event handler callback when
  587. the SAPP_EVENTTYPE_QUIT_REQUESTED event has been received.
  588. - SAPP_EVENTTYPE_QUIT_REQUESTED: this event is sent when the user
  589. clicks the window's close button or application code calls the
  590. sapp_request_quit() function. The event handler callback code can handle
  591. this event by calling sapp_cancel_quit() to cancel the quit.
  592. If the event is ignored, the application will quit as usual.
  593. On the web platform, the quit behaviour differs from native platforms,
  594. because of web-specific restrictions:
  595. A `programmatic quit` initiated by calling sapp_quit() or
  596. sapp_request_quit() will work as described above: the cleanup callback is
  597. called, platform-specific cleanup is performed (on the web
  598. this means that JS event handlers are unregisters), and then
  599. the request-animation-loop will be exited. However that's all. The
  600. web page itself will continue to exist (e.g. it's not possible to
  601. programmatically close the browser tab).
  602. On the web it's also not possible to run custom code when the user
  603. closes a brower tab, so it's not possible to prevent this with a
  604. fancy custom dialog box.
  605. Instead the standard "Leave Site?" dialog box can be activated (or
  606. deactivated) with the following function:
  607. sapp_html5_ask_leave_site(bool ask);
  608. The initial state of the associated internal flag can be provided
  609. at startup via sapp_desc.html5_ask_leave_site.
  610. This feature should only be used sparingly in critical situations - for
  611. instance when the user would loose data - since popping up modal dialog
  612. boxes is considered quite rude in the web world. Note that there's no way
  613. to customize the content of this dialog box or run any code as a result
  614. of the user's decision. Also note that the user must have interacted with
  615. the site before the dialog box will appear. These are all security measures
  616. to prevent fishing.
  617. The Dear ImGui HighDPI sample contains example code of how to
  618. implement a 'Really Quit?' dialog box with Dear ImGui (native desktop
  619. platforms only), and for showing the hardwired "Leave Site?" dialog box
  620. when running on the web platform:
  621. https://floooh.github.io/sokol-html5/wasm/imgui-highdpi-sapp.html
  622. FULLSCREEN
  623. ==========
  624. If the sapp_desc.fullscreen flag is true, sokol-app will try to create
  625. a fullscreen window on platforms with a 'proper' window system
  626. (mobile devices will always use fullscreen). The implementation details
  627. depend on the target platform, in general sokol-app will use a
  628. 'soft approach' which doesn't interfere too much with the platform's
  629. window system (for instance borderless fullscreen window instead of
  630. a 'real' fullscreen mode). Such details might change over time
  631. as sokol-app is adapted for different needs.
  632. The most important effect of fullscreen mode to keep in mind is that
  633. the requested canvas width and height will be ignored for the initial
  634. window size, calling sapp_width() and sapp_height() will instead return
  635. the resolution of the fullscreen canvas (however the provided size
  636. might still be used for the non-fullscreen window, in case the user can
  637. switch back from fullscreen- to windowed-mode).
  638. To toggle fullscreen mode programmatically, call sapp_toggle_fullscreen().
  639. To check if the application window is currently in fullscreen mode,
  640. call sapp_is_fullscreen().
  641. WINDOW ICON SUPPORT
  642. ===================
  643. Some sokol_app.h backends allow to change the window icon programmatically:
  644. - on Win32: the small icon in the window's title bar, and the
  645. bigger icon in the task bar
  646. - on Linux: highly dependent on the used window manager, but usually
  647. the window's title bar icon and/or the task bar icon
  648. - on HTML5: the favicon shown in the page's browser tab
  649. NOTE that it is not possible to set the actual application icon which is
  650. displayed by the operating system on the desktop or 'home screen'. Those
  651. icons must be provided 'traditionally' through operating-system-specific
  652. resources which are associated with the application (sokol_app.h might
  653. later support setting the window icon from platform specific resource data
  654. though).
  655. There are two ways to set the window icon:
  656. - at application start in the sokol_main() function by initializing
  657. the sapp_desc.icon nested struct
  658. - or later by calling the function sapp_set_icon()
  659. As a convenient shortcut, sokol_app.h comes with a builtin default-icon
  660. (a rainbow-colored 'S', which at least looks a bit better than the Windows
  661. default icon for applications), which can be activated like this:
  662. At startup in sokol_main():
  663. sapp_desc sokol_main(...) {
  664. return (sapp_desc){
  665. ...
  666. icon.sokol_default = true
  667. };
  668. }
  669. Or later by calling:
  670. sapp_set_icon(&(sapp_icon_desc){ .sokol_default = true });
  671. NOTE that a completely zero-initialized sapp_icon_desc struct will not
  672. update the window icon in any way. This is an 'escape hatch' so that you
  673. can handle the window icon update yourself (or if you do this already,
  674. sokol_app.h won't get in your way, in this case just leave the
  675. sapp_desc.icon struct zero-initialized).
  676. Providing your own icon images works exactly like in GLFW (down to the
  677. data format):
  678. You provide one or more 'candidate images' in different sizes, and the
  679. sokol_app.h platform backends pick the best match for the specific backend
  680. and icon type.
  681. For each candidate image, you need to provide:
  682. - the width in pixels
  683. - the height in pixels
  684. - and the actual pixel data in RGBA8 pixel format (e.g. 0xFFCC8844
  685. on a little-endian CPU means: alpha=0xFF, blue=0xCC, green=0x88, red=0x44)
  686. For instance, if you have 3 candidate images (small, medium, big) of
  687. sizes 16x16, 32x32 and 64x64 the corresponding sapp_icon_desc struct is setup
  688. like this:
  689. // the actual pixel data (RGBA8, origin top-left)
  690. const uint32_t small[16][16] = { ... };
  691. const uint32_t medium[32][32] = { ... };
  692. const uint32_t big[64][64] = { ... };
  693. const sapp_icon_desc icon_desc = {
  694. .images = {
  695. { .width = 16, .height = 16, .pixels = SAPP_RANGE(small) },
  696. { .width = 32, .height = 32, .pixels = SAPP_RANGE(medium) },
  697. // ...or without the SAPP_RANGE helper macro:
  698. { .width = 64, .height = 64, .pixels = { .ptr=big, .size=sizeof(big) } }
  699. }
  700. };
  701. An sapp_icon_desc struct initialized like this can then either be applied
  702. at application start in sokol_main:
  703. sapp_desc sokol_main(...) {
  704. return (sapp_desc){
  705. ...
  706. icon = icon_desc
  707. };
  708. }
  709. ...or later by calling sapp_set_icon():
  710. sapp_set_icon(&icon_desc);
  711. Some window icon caveats:
  712. - once the window icon has been updated, there's no way to go back to
  713. the platform's default icon, this is because some platforms (Linux
  714. and HTML5) don't switch the icon visual back to the default even if
  715. the custom icon is deleted or removed
  716. - on HTML5, if the sokol_app.h icon doesn't show up in the browser
  717. tab, check that there's no traditional favicon 'link' element
  718. is defined in the page's index.html, sokol_app.h will only
  719. append a new favicon link element, but not delete any manually
  720. defined favicon in the page
  721. For an example and test of the window icon feature, check out the the
  722. 'icon-sapp' sample on the sokol-samples git repository.
  723. ONSCREEN KEYBOARD
  724. =================
  725. On some platforms which don't provide a physical keyboard, sokol-app
  726. can display the platform's integrated onscreen keyboard for text
  727. input. To request that the onscreen keyboard is shown, call
  728. sapp_show_keyboard(true);
  729. Likewise, to hide the keyboard call:
  730. sapp_show_keyboard(false);
  731. Note that on the web platform, the keyboard can only be shown from
  732. inside an input handler. On such platforms, sapp_show_keyboard()
  733. will only work as expected when it is called from inside the
  734. sokol-app event callback function. When called from other places,
  735. an internal flag will be set, and the onscreen keyboard will be
  736. called at the next 'legal' opportunity (when the next input event
  737. is handled).
  738. OPTIONAL: DON'T HIJACK main() (#define SOKOL_NO_ENTRY)
  739. ======================================================
  740. In its default configuration, sokol_app.h "hijacks" the platform's
  741. standard main() function. This was done because different platforms
  742. have different main functions which are not compatible with
  743. C's main() (for instance WinMain on Windows has completely different
  744. arguments). However, this "main hijacking" posed a problem for
  745. usage scenarios like integrating sokol_app.h with other languages than
  746. C or C++, so an alternative SOKOL_NO_ENTRY mode has been added
  747. in which the user code provides the platform's main function:
  748. - define SOKOL_NO_ENTRY before including the sokol_app.h implementation
  749. - do *not* provide a sokol_main() function
  750. - instead provide the standard main() function of the platform
  751. - from the main function, call the function ```sapp_run()``` which
  752. takes a pointer to an ```sapp_desc``` structure.
  753. - ```sapp_run()``` takes over control and calls the provided init-, frame-,
  754. shutdown- and event-callbacks just like in the default model, it
  755. will only return when the application quits (or not at all on some
  756. platforms, like emscripten)
  757. NOTE: SOKOL_NO_ENTRY is currently not supported on Android.
  758. WINDOWS CONSOLE OUTPUT
  759. ======================
  760. On Windows, regular windowed applications don't show any stdout/stderr text
  761. output, which can be a bit of a hassle for printf() debugging or generally
  762. logging text to the console. Also, console output by default uses a local
  763. codepage setting and thus international UTF-8 encoded text is printed
  764. as garbage.
  765. To help with these issues, sokol_app.h can be configured at startup
  766. via the following Windows-specific sapp_desc flags:
  767. sapp_desc.win32_console_utf8 (default: false)
  768. When set to true, the output console codepage will be switched
  769. to UTF-8 (and restored to the original codepage on exit)
  770. sapp_desc.win32_console_attach (default: false)
  771. When set to true, stdout and stderr will be attached to the
  772. console of the parent process (if the parent process actually
  773. has a console). This means that if the application was started
  774. in a command line window, stdout and stderr output will be printed
  775. to the terminal, just like a regular command line program. But if
  776. the application is started via double-click, it will behave like
  777. a regular UI application, and stdout/stderr will not be visible.
  778. sapp_desc.win32_console_create (default: false)
  779. When set to true, a new console window will be created and
  780. stdout/stderr will be redirected to that console window. It
  781. doesn't matter if the application is started from the command
  782. line or via double-click.
  783. TEMP NOTE DUMP
  784. ==============
  785. - onscreen keyboard support on Android requires Java :(, should we even bother?
  786. - sapp_desc needs a bool whether to initialize depth-stencil surface
  787. - GL context initialization needs more control (at least what GL version to initialize)
  788. - application icon
  789. - the UPDATE_CURSOR event currently behaves differently between Win32 and OSX
  790. (Win32 sends the event each frame when the mouse moves and is inside the window
  791. client area, OSX sends it only once when the mouse enters the client area)
  792. - the Android implementation calls cleanup_cb() and destroys the egl context in onDestroy
  793. at the latest but should do it earlier, in onStop, as an app is "killable" after onStop
  794. on Android Honeycomb and later (it can't be done at the moment as the app may be started
  795. again after onStop and the sokol lifecycle does not yet handle context teardown/bringup)
  796. LICENSE
  797. =======
  798. zlib/libpng license
  799. Copyright (c) 2018 Andre Weissflog
  800. This software is provided 'as-is', without any express or implied warranty.
  801. In no event will the authors be held liable for any damages arising from the
  802. use of this software.
  803. Permission is granted to anyone to use this software for any purpose,
  804. including commercial applications, and to alter it and redistribute it
  805. freely, subject to the following restrictions:
  806. 1. The origin of this software must not be misrepresented; you must not
  807. claim that you wrote the original software. If you use this software in a
  808. product, an acknowledgment in the product documentation would be
  809. appreciated but is not required.
  810. 2. Altered source versions must be plainly marked as such, and must not
  811. be misrepresented as being the original software.
  812. 3. This notice may not be removed or altered from any source
  813. distribution.
  814. */
  815. #define SOKOL_APP_INCLUDED (1)
  816. #include <stddef.h> // size_t
  817. #include <stdint.h>
  818. #include <stdbool.h>
  819. #if defined(SOKOL_API_DECL) && !defined(SOKOL_APP_API_DECL)
  820. #define SOKOL_APP_API_DECL SOKOL_API_DECL
  821. #endif
  822. #ifndef SOKOL_APP_API_DECL
  823. #if defined(_WIN32) && defined(SOKOL_DLL) && defined(SOKOL_APP_IMPL)
  824. #define SOKOL_APP_API_DECL __declspec(dllexport)
  825. #elif defined(_WIN32) && defined(SOKOL_DLL)
  826. #define SOKOL_APP_API_DECL __declspec(dllimport)
  827. #else
  828. #define SOKOL_APP_API_DECL extern
  829. #endif
  830. #endif
  831. #ifdef __cplusplus
  832. extern "C" {
  833. #endif
  834. /* misc constants */
  835. enum {
  836. SAPP_INVALID_ID = 0,
  837. SAPP_MAX_TOUCHPOINTS = 8,
  838. SAPP_MAX_MOUSEBUTTONS = 3,
  839. SAPP_MAX_KEYCODES = 512,
  840. SAPP_MAX_ICONIMAGES = 8,
  841. };
  842. /* a window handle */
  843. typedef struct sapp_window { uint32_t id; } sapp_window;
  844. /*
  845. sapp_event_type
  846. The type of event that's passed to the event handler callback
  847. in the sapp_event.type field. These are not just "traditional"
  848. input events, but also notify the application about state changes
  849. or other user-invoked actions.
  850. */
  851. typedef enum sapp_event_type {
  852. SAPP_EVENTTYPE_INVALID,
  853. SAPP_EVENTTYPE_KEY_DOWN,
  854. SAPP_EVENTTYPE_KEY_UP,
  855. SAPP_EVENTTYPE_CHAR,
  856. SAPP_EVENTTYPE_MOUSE_DOWN,
  857. SAPP_EVENTTYPE_MOUSE_UP,
  858. SAPP_EVENTTYPE_MOUSE_SCROLL,
  859. SAPP_EVENTTYPE_MOUSE_MOVE,
  860. SAPP_EVENTTYPE_MOUSE_ENTER,
  861. SAPP_EVENTTYPE_MOUSE_LEAVE,
  862. SAPP_EVENTTYPE_TOUCHES_BEGAN,
  863. SAPP_EVENTTYPE_TOUCHES_MOVED,
  864. SAPP_EVENTTYPE_TOUCHES_ENDED,
  865. SAPP_EVENTTYPE_TOUCHES_CANCELLED,
  866. SAPP_EVENTTYPE_RESIZED,
  867. SAPP_EVENTTYPE_ICONIFIED,
  868. SAPP_EVENTTYPE_RESTORED,
  869. SAPP_EVENTTYPE_SUSPENDED,
  870. SAPP_EVENTTYPE_RESUMED,
  871. SAPP_EVENTTYPE_UPDATE_CURSOR,
  872. SAPP_EVENTTYPE_QUIT_REQUESTED,
  873. SAPP_EVENTTYPE_CLIPBOARD_PASTED,
  874. SAPP_EVENTTYPE_FILES_DROPPED,
  875. SAPP_EVENTTYPE_WINDOW_CLOSED,
  876. _SAPP_EVENTTYPE_NUM,
  877. _SAPP_EVENTTYPE_FORCE_U32 = 0x7FFFFFFF
  878. } sapp_event_type;
  879. /*
  880. sapp_keycode
  881. The 'virtual keycode' of a KEY_DOWN or KEY_UP event in the
  882. struct field sapp_event.key_code.
  883. Note that the keycode values are identical with GLFW.
  884. */
  885. typedef enum sapp_keycode {
  886. SAPP_KEYCODE_INVALID = 0,
  887. SAPP_KEYCODE_SPACE = 32,
  888. SAPP_KEYCODE_APOSTROPHE = 39, /* ' */
  889. SAPP_KEYCODE_COMMA = 44, /* , */
  890. SAPP_KEYCODE_MINUS = 45, /* - */
  891. SAPP_KEYCODE_PERIOD = 46, /* . */
  892. SAPP_KEYCODE_SLASH = 47, /* / */
  893. SAPP_KEYCODE_0 = 48,
  894. SAPP_KEYCODE_1 = 49,
  895. SAPP_KEYCODE_2 = 50,
  896. SAPP_KEYCODE_3 = 51,
  897. SAPP_KEYCODE_4 = 52,
  898. SAPP_KEYCODE_5 = 53,
  899. SAPP_KEYCODE_6 = 54,
  900. SAPP_KEYCODE_7 = 55,
  901. SAPP_KEYCODE_8 = 56,
  902. SAPP_KEYCODE_9 = 57,
  903. SAPP_KEYCODE_SEMICOLON = 59, /* ; */
  904. SAPP_KEYCODE_EQUAL = 61, /* = */
  905. SAPP_KEYCODE_A = 65,
  906. SAPP_KEYCODE_B = 66,
  907. SAPP_KEYCODE_C = 67,
  908. SAPP_KEYCODE_D = 68,
  909. SAPP_KEYCODE_E = 69,
  910. SAPP_KEYCODE_F = 70,
  911. SAPP_KEYCODE_G = 71,
  912. SAPP_KEYCODE_H = 72,
  913. SAPP_KEYCODE_I = 73,
  914. SAPP_KEYCODE_J = 74,
  915. SAPP_KEYCODE_K = 75,
  916. SAPP_KEYCODE_L = 76,
  917. SAPP_KEYCODE_M = 77,
  918. SAPP_KEYCODE_N = 78,
  919. SAPP_KEYCODE_O = 79,
  920. SAPP_KEYCODE_P = 80,
  921. SAPP_KEYCODE_Q = 81,
  922. SAPP_KEYCODE_R = 82,
  923. SAPP_KEYCODE_S = 83,
  924. SAPP_KEYCODE_T = 84,
  925. SAPP_KEYCODE_U = 85,
  926. SAPP_KEYCODE_V = 86,
  927. SAPP_KEYCODE_W = 87,
  928. SAPP_KEYCODE_X = 88,
  929. SAPP_KEYCODE_Y = 89,
  930. SAPP_KEYCODE_Z = 90,
  931. SAPP_KEYCODE_LEFT_BRACKET = 91, /* [ */
  932. SAPP_KEYCODE_BACKSLASH = 92, /* \ */
  933. SAPP_KEYCODE_RIGHT_BRACKET = 93, /* ] */
  934. SAPP_KEYCODE_GRAVE_ACCENT = 96, /* ` */
  935. SAPP_KEYCODE_WORLD_1 = 161, /* non-US #1 */
  936. SAPP_KEYCODE_WORLD_2 = 162, /* non-US #2 */
  937. SAPP_KEYCODE_ESCAPE = 256,
  938. SAPP_KEYCODE_ENTER = 257,
  939. SAPP_KEYCODE_TAB = 258,
  940. SAPP_KEYCODE_BACKSPACE = 259,
  941. SAPP_KEYCODE_INSERT = 260,
  942. SAPP_KEYCODE_DELETE = 261,
  943. SAPP_KEYCODE_RIGHT = 262,
  944. SAPP_KEYCODE_LEFT = 263,
  945. SAPP_KEYCODE_DOWN = 264,
  946. SAPP_KEYCODE_UP = 265,
  947. SAPP_KEYCODE_PAGE_UP = 266,
  948. SAPP_KEYCODE_PAGE_DOWN = 267,
  949. SAPP_KEYCODE_HOME = 268,
  950. SAPP_KEYCODE_END = 269,
  951. SAPP_KEYCODE_CAPS_LOCK = 280,
  952. SAPP_KEYCODE_SCROLL_LOCK = 281,
  953. SAPP_KEYCODE_NUM_LOCK = 282,
  954. SAPP_KEYCODE_PRINT_SCREEN = 283,
  955. SAPP_KEYCODE_PAUSE = 284,
  956. SAPP_KEYCODE_F1 = 290,
  957. SAPP_KEYCODE_F2 = 291,
  958. SAPP_KEYCODE_F3 = 292,
  959. SAPP_KEYCODE_F4 = 293,
  960. SAPP_KEYCODE_F5 = 294,
  961. SAPP_KEYCODE_F6 = 295,
  962. SAPP_KEYCODE_F7 = 296,
  963. SAPP_KEYCODE_F8 = 297,
  964. SAPP_KEYCODE_F9 = 298,
  965. SAPP_KEYCODE_F10 = 299,
  966. SAPP_KEYCODE_F11 = 300,
  967. SAPP_KEYCODE_F12 = 301,
  968. SAPP_KEYCODE_F13 = 302,
  969. SAPP_KEYCODE_F14 = 303,
  970. SAPP_KEYCODE_F15 = 304,
  971. SAPP_KEYCODE_F16 = 305,
  972. SAPP_KEYCODE_F17 = 306,
  973. SAPP_KEYCODE_F18 = 307,
  974. SAPP_KEYCODE_F19 = 308,
  975. SAPP_KEYCODE_F20 = 309,
  976. SAPP_KEYCODE_F21 = 310,
  977. SAPP_KEYCODE_F22 = 311,
  978. SAPP_KEYCODE_F23 = 312,
  979. SAPP_KEYCODE_F24 = 313,
  980. SAPP_KEYCODE_F25 = 314,
  981. SAPP_KEYCODE_KP_0 = 320,
  982. SAPP_KEYCODE_KP_1 = 321,
  983. SAPP_KEYCODE_KP_2 = 322,
  984. SAPP_KEYCODE_KP_3 = 323,
  985. SAPP_KEYCODE_KP_4 = 324,
  986. SAPP_KEYCODE_KP_5 = 325,
  987. SAPP_KEYCODE_KP_6 = 326,
  988. SAPP_KEYCODE_KP_7 = 327,
  989. SAPP_KEYCODE_KP_8 = 328,
  990. SAPP_KEYCODE_KP_9 = 329,
  991. SAPP_KEYCODE_KP_DECIMAL = 330,
  992. SAPP_KEYCODE_KP_DIVIDE = 331,
  993. SAPP_KEYCODE_KP_MULTIPLY = 332,
  994. SAPP_KEYCODE_KP_SUBTRACT = 333,
  995. SAPP_KEYCODE_KP_ADD = 334,
  996. SAPP_KEYCODE_KP_ENTER = 335,
  997. SAPP_KEYCODE_KP_EQUAL = 336,
  998. SAPP_KEYCODE_LEFT_SHIFT = 340,
  999. SAPP_KEYCODE_LEFT_CONTROL = 341,
  1000. SAPP_KEYCODE_LEFT_ALT = 342,
  1001. SAPP_KEYCODE_LEFT_SUPER = 343,
  1002. SAPP_KEYCODE_RIGHT_SHIFT = 344,
  1003. SAPP_KEYCODE_RIGHT_CONTROL = 345,
  1004. SAPP_KEYCODE_RIGHT_ALT = 346,
  1005. SAPP_KEYCODE_RIGHT_SUPER = 347,
  1006. SAPP_KEYCODE_MENU = 348,
  1007. } sapp_keycode;
  1008. /*
  1009. sapp_touchpoint
  1010. Describes a single touchpoint in a multitouch event (TOUCHES_BEGAN,
  1011. TOUCHES_MOVED, TOUCHES_ENDED).
  1012. Touch points are stored in the nested array sapp_event.touches[],
  1013. and the number of touches is stored in sapp_event.num_touches.
  1014. */
  1015. typedef struct sapp_touchpoint {
  1016. uintptr_t identifier;
  1017. float pos_x;
  1018. float pos_y;
  1019. bool changed;
  1020. } sapp_touchpoint;
  1021. /*
  1022. sapp_mousebutton
  1023. The currently pressed mouse button in the events MOUSE_DOWN
  1024. and MOUSE_UP, stored in the struct field sapp_event.mouse_button.
  1025. */
  1026. typedef enum sapp_mousebutton {
  1027. SAPP_MOUSEBUTTON_LEFT = 0x0,
  1028. SAPP_MOUSEBUTTON_RIGHT = 0x1,
  1029. SAPP_MOUSEBUTTON_MIDDLE = 0x2,
  1030. SAPP_MOUSEBUTTON_INVALID = 0x100,
  1031. } sapp_mousebutton;
  1032. /*
  1033. These are currently pressed modifier keys (and mouse buttons) which are
  1034. passed in the event struct field sapp_event.modifiers.
  1035. */
  1036. enum {
  1037. SAPP_MODIFIER_SHIFT = 0x1, // left or right shift key
  1038. SAPP_MODIFIER_CTRL = 0x2, // left or right control key
  1039. SAPP_MODIFIER_ALT = 0x4, // left or right alt key
  1040. SAPP_MODIFIER_SUPER = 0x8, // left or right 'super' key
  1041. SAPP_MODIFIER_LMB = 0x100, // left mouse button
  1042. SAPP_MODIFIER_RMB = 0x200, // right mouse button
  1043. SAPP_MODIFIER_MMB = 0x400, // middle mouse button
  1044. };
  1045. /*
  1046. sapp_event
  1047. This is an all-in-one event struct passed to the event handler
  1048. user callback function. Note that it depends on the event
  1049. type what struct fields actually contain useful values, so you
  1050. should first check the event type before reading other struct
  1051. fields.
  1052. */
  1053. typedef struct sapp_event {
  1054. uint64_t frame_count; // current frame counter, always valid, useful for checking if two events were issued in the same frame
  1055. sapp_window window; // the window this event is associated with
  1056. sapp_event_type type; // the event type, always valid
  1057. sapp_keycode key_code; // the virtual key code, only valid in KEY_UP, KEY_DOWN
  1058. uint32_t char_code; // the UTF-32 character code, only valid in CHAR events
  1059. bool key_repeat; // true if this is a key-repeat event, valid in KEY_UP, KEY_DOWN and CHAR
  1060. uint32_t modifiers; // current modifier keys, valid in all key-, char- and mouse-events
  1061. sapp_mousebutton mouse_button; // mouse button that was pressed or released, valid in MOUSE_DOWN, MOUSE_UP
  1062. float mouse_x; // current horizontal mouse position in pixels, always valid except during mouse lock
  1063. float mouse_y; // current vertical mouse position in pixels, always valid except during mouse lock
  1064. float mouse_dx; // relative horizontal mouse movement since last frame, always valid
  1065. float mouse_dy; // relative vertical mouse movement since last frame, always valid
  1066. float scroll_x; // horizontal mouse wheel scroll distance, valid in MOUSE_SCROLL events
  1067. float scroll_y; // vertical mouse wheel scroll distance, valid in MOUSE_SCROLL events
  1068. int num_touches; // number of valid items in the touches[] array
  1069. sapp_touchpoint touches[SAPP_MAX_TOUCHPOINTS]; // current touch points, valid in TOUCHES_BEGIN, TOUCHES_MOVED, TOUCHES_ENDED
  1070. int window_width; // current window- and framebuffer sizes in pixels, always valid
  1071. int window_height;
  1072. int framebuffer_width; // = window_width * dpi_scale
  1073. int framebuffer_height; // = window_height * dpi_scale
  1074. } sapp_event;
  1075. /*
  1076. sg_range
  1077. A general pointer/size-pair struct and constructor macros for passing binary blobs
  1078. into sokol_app.h.
  1079. */
  1080. typedef struct sapp_range {
  1081. const void* ptr;
  1082. size_t size;
  1083. } sapp_range;
  1084. // disabling this for every includer isn't great, but the warnings are also quite pointless
  1085. #if defined(_MSC_VER)
  1086. #pragma warning(disable:4221) /* /W4 only: nonstandard extension used: 'x': cannot be initialized using address of automatic variable 'y' */
  1087. #pragma warning(disable:4204) /* VS2015: nonstandard extension used: non-constant aggregate initializer */
  1088. #endif
  1089. #if defined(__cplusplus)
  1090. #define SAPP_RANGE(x) sapp_range{ &x, sizeof(x) }
  1091. #else
  1092. #define SAPP_RANGE(x) (sapp_range){ &x, sizeof(x) }
  1093. #endif
  1094. /*
  1095. sapp_image_desc
  1096. This is used to describe image data to sokol_app.h (at first, window
  1097. icons, later maybe cursor images).
  1098. Note that the actual image pixel format depends on the use case:
  1099. - window icon pixels are RGBA8
  1100. - cursor images are ??? (FIXME)
  1101. */
  1102. typedef struct sapp_image_desc {
  1103. int width;
  1104. int height;
  1105. sapp_range pixels;
  1106. } sapp_image_desc;
  1107. /*
  1108. sapp_icon_desc
  1109. An icon description structure for use in sapp_desc.icon and
  1110. sapp_set_icon().
  1111. When setting a custom image, the application can provide a number of
  1112. candidates differing in size, and sokol_app.h will pick the image(s)
  1113. closest to the size expected by the platform's window system.
  1114. To set sokol-app's default icon, set .sokol_default to true.
  1115. Otherwise provide candidate images of different sizes in the
  1116. images[] array.
  1117. If both the sokol_default flag is set to true, any image candidates
  1118. will be ignored and the sokol_app.h default icon will be set.
  1119. */
  1120. typedef struct sapp_icon_desc {
  1121. bool sokol_default;
  1122. sapp_image_desc images[SAPP_MAX_ICONIMAGES];
  1123. } sapp_icon_desc;
  1124. /*
  1125. sapp_window_desc
  1126. FIXME: docs
  1127. */
  1128. typedef struct sapp_window_desc {
  1129. const char* title; // the window title as UTF-8 encoded string
  1130. int x; // window position (if 0,0 a good default might be chosen)
  1131. int y;
  1132. int width; // window size
  1133. int height;
  1134. int sample_count; // MSAA sample count
  1135. int swap_interval; // the preferred swap interval (ignored on some platforms)
  1136. bool high_dpi; // whether the rendering canvas is full-resolution on HighDPI displays
  1137. bool fullscreen; // whether the window should be created in fullscreen mode
  1138. bool alpha; // whether the framebuffer should have an alpha channel (ignored on some platforms)
  1139. bool user_cursor; // if true, user is expected to manage cursor image in SAPP_EVENTTYPE_UPDATE_CURSOR
  1140. bool no_decoration; // create window without window system decorations (title bar etc...)
  1141. bool hidden; // create window in hidden state
  1142. void* user_data;
  1143. } sapp_window_desc;
  1144. typedef struct sapp_gl_desc {
  1145. bool force_gles2; // if true, setup GLES2/WebGL even if GLES3/WebGL2 is available
  1146. } sapp_gl_desc;
  1147. typedef struct sapp_win32_desc {
  1148. bool console_utf8; // if true, set the output console codepage to UTF-8
  1149. bool console_create; // if true, attach stdout/stderr to a new console window
  1150. bool console_attach; // if true, attach stdout/stderr to parent process
  1151. } sapp_win32_desc;
  1152. typedef struct sapp_html5_desc {
  1153. const char* canvas_name; // the name (id) of the HTML5 canvas element, default is "canvas"
  1154. bool canvas_resize; // if true, the HTML5 canvas size is set to sapp_desc.width/height, otherwise canvas size is tracked
  1155. bool preserve_drawing_buffer; // HTML5 only: whether to preserve default framebuffer content between frames
  1156. bool premultiplied_alpha; // HTML5 only: whether the rendered pixels use premultiplied alpha convention
  1157. bool ask_leave_site; // initial state of the internal html5_ask_leave_site flag (see sapp_html5_ask_leave_site())
  1158. } sapp_html5_desc;
  1159. typedef struct sapp_ios_desc {
  1160. bool keyboard_resizes_canvas; // if true, showing the iOS keyboard shrinks the canvas
  1161. } sapp_ios_desc;
  1162. typedef struct sapp_desc {
  1163. void (*init_cb)(void); // these are the user-provided callbacks without user data
  1164. void (*frame_cb)(void);
  1165. void (*cleanup_cb)(void);
  1166. void (*event_cb)(const sapp_event*);
  1167. void (*init_userdata_cb)(void*); // these are the user-provided callbacks with user data
  1168. void (*frame_userdata_cb)(void*);
  1169. void (*cleanup_userdata_cb)(void*);
  1170. void (*event_userdata_cb)(const sapp_event*, void*);
  1171. void* user_data;
  1172. int window_pool_size;
  1173. const char* window_title; // the window title as UTF-8 encoded string
  1174. int x;
  1175. int y;
  1176. int width;
  1177. int height;
  1178. int sample_count; // MSAA sample count
  1179. int swap_interval; // the preferred swap interval (ignored on some platforms)
  1180. bool high_dpi; // whether the rendering canvas is full-resolution on HighDPI displays
  1181. bool fullscreen; // whether the window should be created in fullscreen mode
  1182. bool alpha; // whether the framebuffer should have an alpha channel (ignored on some platforms)
  1183. bool user_cursor; // if true, user is expected to manage cursor image in SAPP_EVENTTYPE_UPDATE_CURSOR
  1184. bool no_decoration; // create main window without window system decorations (title bar etc...)
  1185. bool enable_dragndrop; // enable file dropping (drag'n'drop), default is false
  1186. int max_dropped_files; // max number of dropped files to process (default: 1)
  1187. int max_dropped_file_path_length; // max length in bytes of a dropped UTF-8 file path (default: 2048)
  1188. bool enable_clipboard;
  1189. int clipboard_size;
  1190. sapp_icon_desc icon;
  1191. sapp_gl_desc gl;
  1192. sapp_win32_desc win32;
  1193. sapp_html5_desc html5;
  1194. sapp_ios_desc ios;
  1195. // FIXME: fail callback for error handling isn't all that useful...
  1196. void* fail_user_data;
  1197. void (*fail_cb)(const char*);
  1198. void (*fail_userdata_cb)(const char*, void*);
  1199. } sapp_desc;
  1200. /* HTML5 specific: request and response structs for
  1201. asynchronously loading dropped-file content.
  1202. */
  1203. typedef enum sapp_html5_fetch_error {
  1204. SAPP_HTML5_FETCH_ERROR_NO_ERROR,
  1205. SAPP_HTML5_FETCH_ERROR_BUFFER_TOO_SMALL,
  1206. SAPP_HTML5_FETCH_ERROR_OTHER,
  1207. } sapp_html5_fetch_error;
  1208. typedef struct sapp_html5_fetch_response {
  1209. bool succeeded; /* true if the loading operation has succeeded */
  1210. sapp_html5_fetch_error error_code;
  1211. int file_index; /* index of the dropped file (0..sapp_get_num_dropped_filed()-1) */
  1212. uint32_t fetched_size; /* size in bytes of loaded data */
  1213. void* buffer_ptr; /* pointer to user-provided buffer which contains the loaded data */
  1214. uint32_t buffer_size; /* size of user-provided buffer (buffer_size >= fetched_size) */
  1215. void* user_data; /* user-provided user data pointer */
  1216. } sapp_html5_fetch_response;
  1217. typedef struct sapp_html5_fetch_request {
  1218. int dropped_file_index; /* 0..sapp_get_num_dropped_files()-1 */
  1219. void (*callback)(const sapp_html5_fetch_response*); /* response callback function pointer (required) */
  1220. void* buffer_ptr; /* pointer to buffer to load data into */
  1221. uint32_t buffer_size; /* size in bytes of buffer */
  1222. void* user_data; /* optional userdata pointer */
  1223. } sapp_html5_fetch_request;
  1224. /* user-provided functions */
  1225. extern sapp_desc sokol_main(int argc, char* argv[]);
  1226. /* special run-function for SOKOL_NO_ENTRY (in standard mode this is an empty stub) */
  1227. SOKOL_APP_API_DECL void sapp_run(const sapp_desc* desc);
  1228. /* returns true after sokol-app has been initialized */
  1229. SOKOL_APP_API_DECL bool sapp_isvalid(void);
  1230. /* return a copy of the sapp_desc structure */
  1231. SOKOL_APP_API_DECL sapp_desc sapp_query_desc(void);
  1232. /* get the current frame counter (for comparison with sapp_event.frame_count) */
  1233. SOKOL_APP_API_DECL uint64_t sapp_frame_count(void);
  1234. /* get main window's color pixel format */
  1235. SOKOL_APP_API_DECL int sapp_color_format(void);
  1236. /* get main window's depth pixel format */
  1237. SOKOL_APP_API_DECL int sapp_depth_format(void);
  1238. /* initiate a "soft quit" (sends SAPP_EVENTTYPE_QUIT_REQUESTED) */
  1239. SOKOL_APP_API_DECL void sapp_request_quit(void);
  1240. /* cancel a pending quit (when SAPP_EVENTTYPE_QUIT_REQUESTED has been received) */
  1241. SOKOL_APP_API_DECL void sapp_cancel_quit(void);
  1242. /* initiate a "hard quit" (quit application without sending SAPP_EVENTTYPE_QUIT_REQUSTED) */
  1243. SOKOL_APP_API_DECL void sapp_quit(void);
  1244. /* call from inside event callback to consume the current event (don't forward to platform) */
  1245. SOKOL_APP_API_DECL void sapp_consume_event(void);
  1246. /* show or hide the mobile device onscreen keyboard */
  1247. SOKOL_APP_API_DECL void sapp_show_keyboard(bool show);
  1248. /* return true if the mobile device onscreen keyboard is currently shown */
  1249. SOKOL_APP_API_DECL bool sapp_keyboard_shown(void);
  1250. /* show or hide the mouse cursor */
  1251. SOKOL_APP_API_DECL void sapp_show_mouse(bool show);
  1252. /* show or hide the mouse cursor */
  1253. SOKOL_APP_API_DECL bool sapp_mouse_shown();
  1254. /* enable/disable mouse-pointer-lock mode */
  1255. SOKOL_APP_API_DECL void sapp_lock_mouse(bool lock);
  1256. /* return true if in mouse-pointer-lock mode (this may toggle a few frames later) */
  1257. SOKOL_APP_API_DECL bool sapp_mouse_locked(void);
  1258. /* set application/window icon (only on desktop platforms) */
  1259. SOKOL_APP_API_DECL void sapp_set_icon(const sapp_icon_desc* icon_desc);
  1260. /*=== single-window functions (return properties of the main window) =========*/
  1261. /* get framebuffer width in pixels */
  1262. SOKOL_APP_API_DECL int sapp_width(void);
  1263. /* same as sapp_width(), but returns float */
  1264. SOKOL_APP_API_DECL float sapp_widthf(void);
  1265. /* get framebuffer height in pixels */
  1266. SOKOL_APP_API_DECL int sapp_height(void);
  1267. /* same as sapp_height(), but returns float */
  1268. SOKOL_APP_API_DECL float sapp_heightf(void);
  1269. /* set client rect size in logical 'window system pixels' */
  1270. SOKOL_APP_API_DECL void sapp_set_client_size(int width, int height);
  1271. /* same as sapp_set_client_size(), but with float parameters */
  1272. SOKOL_APP_API_DECL void sapp_set_client_sizef(float width, float height);
  1273. /* get client rect width in logical 'window system pixels' */
  1274. SOKOL_APP_API_DECL int sapp_client_width(void);
  1275. /* same as sapp_client_width() but returns float */
  1276. SOKOL_APP_API_DECL float sapp_client_widthf(void);
  1277. /* get client rect height in logical 'window system pixels' */
  1278. SOKOL_APP_API_DECL int sapp_client_height(void);
  1279. /* same as sapp_client_height() but returns float */
  1280. SOKOL_APP_API_DECL float sapp_client_heightf(void);
  1281. /* set client rect position in logical 'window system pixels' */
  1282. SOKOL_APP_API_DECL void sapp_set_client_pos(int x, int y);
  1283. /* same as sapp_set_client_pos(), but with float parameters */
  1284. SOKOL_APP_API_DECL void sapp_set_client_posf(float x, float y);
  1285. /* get client rect horizontal position in logical 'window system pixels' */
  1286. SOKOL_APP_API_DECL int sapp_client_posx(void);
  1287. /* same as sapp_client_posx(), but returns float */
  1288. SOKOL_APP_API_DECL float sapp_client_posxf(void);
  1289. /* get client rect vertical position in logical 'window system pixels' */
  1290. SOKOL_APP_API_DECL int sapp_client_posy(void);
  1291. /* same as sapp_client_posy(), but returns float */
  1292. SOKOL_APP_API_DECL float sapp_client_posyf(void);
  1293. /* get main window's sample count */
  1294. SOKOL_APP_API_DECL int sapp_sample_count(void);
  1295. /* returns true when high_dpi was requested and actually running in a high-dpi scenario */
  1296. SOKOL_APP_API_DECL bool sapp_high_dpi(void);
  1297. /* returns the dpi scaling factor (window pixels to framebuffer pixels) */
  1298. SOKOL_APP_API_DECL float sapp_dpi_scale(void);
  1299. /* query fullscreen mode */
  1300. SOKOL_APP_API_DECL bool sapp_is_fullscreen(void);
  1301. /* toggle fullscreen mode */
  1302. SOKOL_APP_API_DECL void sapp_toggle_fullscreen(void);
  1303. /* set the main window title (only on desktop platforms) */
  1304. SOKOL_APP_API_DECL void sapp_set_title(const char* str);
  1305. /* get main window userdata pointer */
  1306. SOKOL_APP_API_DECL void* sapp_userdata(void);
  1307. /*=== multi-window functions =================================================*/
  1308. /* open a new window and return window handle */
  1309. SOKOL_APP_API_DECL sapp_window sapp_open_window(const sapp_window_desc* desc);
  1310. /* close a window */
  1311. SOKOL_APP_API_DECL void sapp_close_window(sapp_window window);
  1312. /* start rendering into a window */
  1313. SOKOL_APP_API_DECL void sapp_activate_window_context(sapp_window window);
  1314. /* return an array index for a window handle (to associate your own data with window handle) */
  1315. SOKOL_APP_API_DECL int sapp_window_slot_index(sapp_window window);
  1316. /* get the main window handle */
  1317. SOKOL_APP_API_DECL sapp_window sapp_main_window(void);
  1318. /* start iterating over windows */
  1319. SOKOL_APP_API_DECL sapp_window sapp_first_window(void);
  1320. /* continue iterating over windows, returns invalid handle when finished */
  1321. SOKOL_APP_API_DECL sapp_window sapp_next_window(sapp_window window);
  1322. /* test if a window handle is valid */
  1323. SOKOL_APP_API_DECL bool sapp_window_valid(sapp_window window);
  1324. /* show a hidden window */
  1325. SOKOL_APP_API_DECL void sapp_show_window(sapp_window window);
  1326. /* hide a visible window */
  1327. SOKOL_APP_API_DECL void sapp_hide_window(sapp_window window);
  1328. /* return true if a window is currently visible */
  1329. SOKOL_APP_API_DECL bool sapp_window_visible(sapp_window window);
  1330. /* make window focused (bring to front and get keyboard input focus) */
  1331. SOKOL_APP_API_DECL void sapp_focus_window(sapp_window window);
  1332. /* test if window is the focus window */
  1333. SOKOL_APP_API_DECL bool sapp_window_focused(sapp_window window);
  1334. /* test if window is minimized */
  1335. SOKOL_APP_API_DECL bool sapp_window_minimized(sapp_window window);
  1336. /* get window's framebuffer width in pixels */
  1337. SOKOL_APP_API_DECL int sapp_window_width(sapp_window window);
  1338. /* same as sapp_window_width(), but returns float */
  1339. SOKOL_APP_API_DECL float sapp_window_widthf(sapp_window window);
  1340. /* get window's framebuffer height in pixels */
  1341. SOKOL_APP_API_DECL int sapp_window_height(sapp_window window);
  1342. /* same as sapp_window_height(), but returns float */
  1343. SOKOL_APP_API_DECL float sapp_window_heightf(sapp_window window);
  1344. /* set window's client rect size in logical 'window system pixels' */
  1345. SOKOL_APP_API_DECL void sapp_window_set_client_size(sapp_window window, int width, int height);
  1346. /* same as sapp_window_set_client_size(), but with float parameters */
  1347. SOKOL_APP_API_DECL void sapp_window_set_client_sizef(sapp_window window, float width, float height);
  1348. /* get window's client rect width in logical 'window system pixels' */
  1349. SOKOL_APP_API_DECL int sapp_window_client_width(sapp_window window);
  1350. /* same as sapp_window_client_width() but returns float */
  1351. SOKOL_APP_API_DECL float sapp_window_client_widthf(sapp_window window);
  1352. /* get window's client rect height in logical 'window system pixels' */
  1353. SOKOL_APP_API_DECL int sapp_window_client_height(sapp_window window);
  1354. /* same as sapp_client_height() but returns float */
  1355. SOKOL_APP_API_DECL float sapp_window_client_heightf(sapp_window window);
  1356. /* set window's client rect position in logical 'window system pixels' */
  1357. SOKOL_APP_API_DECL void sapp_window_set_client_pos(sapp_window window, int x, int y);
  1358. /* same as sapp_window_set_client_pos(), but with float parameters */
  1359. SOKOL_APP_API_DECL void sapp_window_set_client_posf(sapp_window window, float x, float y);
  1360. /* get window's client rect horizontal position in logical 'window system pixels' */
  1361. SOKOL_APP_API_DECL int sapp_window_client_posx(sapp_window window);
  1362. /* same as sapp_window_client_posx(), but returns float */
  1363. SOKOL_APP_API_DECL float sapp_window_client_posxf(sapp_window window);
  1364. /* get window's client rect vertical position in logical 'window system pixels' */
  1365. SOKOL_APP_API_DECL int sapp_window_client_posy(sapp_window window);
  1366. /* same as sapp_window_client_posy(), but returns float */
  1367. SOKOL_APP_API_DECL float sapp_window_client_posyf(sapp_window window);
  1368. /* get a window's sample count */
  1369. SOKOL_APP_API_DECL int sapp_window_sample_count(sapp_window window);
  1370. /* returns true when high_dpi was requested for a window, and actually running in a high-dpi scenario */
  1371. SOKOL_APP_API_DECL bool sapp_window_high_dpi(sapp_window window);
  1372. /* returns a window's dpi scaling factor (window pixels to framebuffer pixels) */
  1373. SOKOL_APP_API_DECL float sapp_window_dpi_scale(sapp_window window);
  1374. /* query if a window is currently in fullscreen mode */
  1375. SOKOL_APP_API_DECL bool sapp_window_is_fullscreen(sapp_window window);
  1376. /* toggle a window to and from fullscreen mode */
  1377. SOKOL_APP_API_DECL void sapp_window_toggle_fullscreen(sapp_window window);
  1378. /* set window title (only on desktop platforms) */
  1379. SOKOL_APP_API_DECL void sapp_window_set_title(sapp_window window, const char* str);
  1380. /* get window userdata pointer */
  1381. SOKOL_APP_API_DECL void* sapp_window_userdata(sapp_window window);
  1382. // FIXME FIXME FIXME: are these per window or better global?
  1383. /* write string into clipboard */
  1384. SOKOL_APP_API_DECL void sapp_set_clipboard_string(const char* str);
  1385. /* read string from clipboard (usually during SAPP_EVENTTYPE_CLIPBOARD_PASTED) */
  1386. SOKOL_APP_API_DECL const char* sapp_get_clipboard_string(void);
  1387. /* gets the total number of dropped files (after an SAPP_EVENTTYPE_FILES_DROPPED event) */
  1388. SOKOL_APP_API_DECL int sapp_get_num_dropped_files(void);
  1389. /* gets the dropped file paths */
  1390. SOKOL_APP_API_DECL const char* sapp_get_dropped_file_path(int index);
  1391. /* GL: return true when GLES2 fallback is active (to detect fallback from GLES3) */
  1392. SOKOL_APP_API_DECL bool sapp_gles2(void);
  1393. /* HTML5: enable or disable the hardwired "Leave Site?" dialog box */
  1394. SOKOL_APP_API_DECL void sapp_html5_ask_leave_site(bool ask);
  1395. /* HTML5: get byte size of a dropped file */
  1396. SOKOL_APP_API_DECL uint32_t sapp_html5_get_dropped_file_size(int index);
  1397. /* HTML5: asynchronously load the content of a dropped file */
  1398. SOKOL_APP_API_DECL void sapp_html5_fetch_dropped_file(const sapp_html5_fetch_request* request);
  1399. /* Metal: get bridged pointer to Metal device object */
  1400. SOKOL_APP_API_DECL const void* sapp_metal_get_device(void);
  1401. /* Metal: get bridged pointer to the main window's renderpass descriptor for this frame */
  1402. SOKOL_APP_API_DECL const void* sapp_metal_get_renderpass_descriptor(void);
  1403. /* Metal: get bridged pointer to a window's renderpass descriptor for this frame */
  1404. SOKOL_APP_API_DECL const void* sapp_metal_get_window_renderpass_descriptor(sapp_window window);
  1405. /* Metal: get bridged pointer to the main window's current drawable */
  1406. SOKOL_APP_API_DECL const void* sapp_metal_get_drawable(void);
  1407. /* Metal: get bridged pointer to a window's current drawable */
  1408. SOKOL_APP_API_DECL const void* sapp_metal_get_window_drawable(sapp_window window);
  1409. /* macOS: get bridged pointer to macOS NSWindow */
  1410. SOKOL_APP_API_DECL const void* sapp_macos_get_nswindow(sapp_window window);
  1411. /* iOS: get bridged pointer to iOS UIWindow */
  1412. SOKOL_APP_API_DECL const void* sapp_ios_get_uiwindow(void);
  1413. /* D3D11: get pointer to ID3D11Device object */
  1414. SOKOL_APP_API_DECL const void* sapp_d3d11_get_device(void);
  1415. /* D3D11: get pointer to ID3D11DeviceContext object */
  1416. SOKOL_APP_API_DECL const void* sapp_d3d11_get_device_context(void);
  1417. /* D3D11: get pointer to IDXGISwapChain object */
  1418. SOKOL_APP_API_DECL const void* sapp_d3d11_get_swap_chain(void);
  1419. /* D3D11: get pointer to ID3D11RenderTargetView object */
  1420. SOKOL_APP_API_DECL const void* sapp_d3d11_get_render_target_view(void);
  1421. /* D3D11: get pointer to ID3D11DepthStencilView */
  1422. SOKOL_APP_API_DECL const void* sapp_d3d11_get_depth_stencil_view(void);
  1423. /* Win32: get the HWND window handle */
  1424. SOKOL_APP_API_DECL const void* sapp_win32_get_hwnd(void);
  1425. /* WebGPU: get WGPUDevice handle */
  1426. SOKOL_APP_API_DECL const void* sapp_wgpu_get_device(void);
  1427. /* WebGPU: get swapchain's WGPUTextureView handle for rendering */
  1428. SOKOL_APP_API_DECL const void* sapp_wgpu_get_render_view(void);
  1429. /* WebGPU: get swapchain's MSAA-resolve WGPUTextureView (may return null) */
  1430. SOKOL_APP_API_DECL const void* sapp_wgpu_get_resolve_view(void);
  1431. /* WebGPU: get swapchain's WGPUTextureView for the depth-stencil surface */
  1432. SOKOL_APP_API_DECL const void* sapp_wgpu_get_depth_stencil_view(void);
  1433. /* Android: get native activity handle */
  1434. SOKOL_APP_API_DECL const void* sapp_android_get_native_activity(void);
  1435. #ifdef __cplusplus
  1436. } /* extern "C" */
  1437. /* reference-based equivalents for C++ */
  1438. inline void sapp_run(const sapp_desc& desc) { return sapp_run(&desc); }
  1439. #endif
  1440. // this WinRT specific hack is required when wWinMain is in a static library
  1441. #if defined(_MSC_VER) && defined(UNICODE)
  1442. #include <winapifamily.h>
  1443. #if defined(WINAPI_FAMILY_PARTITION) && !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
  1444. #pragma comment(linker, "/include:wWinMain")
  1445. #endif
  1446. #endif
  1447. #endif // SOKOL_APP_INCLUDED
  1448. /*-- IMPLEMENTATION ----------------------------------------------------------*/
  1449. #ifdef SOKOL_APP_IMPL
  1450. #define SOKOL_APP_IMPL_INCLUDED (1)
  1451. #include <string.h> // memset
  1452. #include <stddef.h> // size_t
  1453. /* check if the config defines are alright */
  1454. #if defined(__APPLE__)
  1455. // see https://clang.llvm.org/docs/LanguageExtensions.html#automatic-reference-counting
  1456. #if !defined(__cplusplus)
  1457. #if __has_feature(objc_arc) && !__has_feature(objc_arc_fields)
  1458. #error "sokol_app.h requires __has_feature(objc_arc_field) if ARC is enabled (use a more recent compiler version)"
  1459. #endif
  1460. #endif
  1461. #define _SAPP_APPLE (1)
  1462. #include <TargetConditionals.h>
  1463. #if defined(TARGET_OS_IPHONE) && !TARGET_OS_IPHONE
  1464. /* MacOS */
  1465. #define _SAPP_MACOS (1)
  1466. #if !defined(SOKOL_METAL) && !defined(SOKOL_GLCORE33)
  1467. #error("sokol_app.h: unknown 3D API selected for MacOS, must be SOKOL_METAL or SOKOL_GLCORE33")
  1468. #endif
  1469. #else
  1470. /* iOS or iOS Simulator */
  1471. #define _SAPP_IOS (1)
  1472. #if !defined(SOKOL_METAL) && !defined(SOKOL_GLES3)
  1473. #error("sokol_app.h: unknown 3D API selected for iOS, must be SOKOL_METAL or SOKOL_GLES3")
  1474. #endif
  1475. #endif
  1476. #elif defined(__EMSCRIPTEN__)
  1477. /* emscripten (asm.js or wasm) */
  1478. #define _SAPP_EMSCRIPTEN (1)
  1479. #if !defined(SOKOL_GLES3) && !defined(SOKOL_GLES2) && !defined(SOKOL_WGPU)
  1480. #error("sokol_app.h: unknown 3D API selected for emscripten, must be SOKOL_GLES3, SOKOL_GLES2 or SOKOL_WGPU")
  1481. #endif
  1482. #elif defined(_WIN32)
  1483. /* Windows (D3D11 or GL) */
  1484. #include <winapifamily.h>
  1485. #if (defined(WINAPI_FAMILY_PARTITION) && !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP))
  1486. #define _SAPP_UWP (1)
  1487. #if !defined(SOKOL_D3D11)
  1488. #error("sokol_app.h: unknown 3D API selected for UWP, must be SOKOL_D3D11")
  1489. #endif
  1490. #if !defined(__cplusplus)
  1491. #error("sokol_app.h: UWP bindings require C++/17")
  1492. #endif
  1493. #else
  1494. #define _SAPP_WIN32 (1)
  1495. #if !defined(SOKOL_D3D11) && !defined(SOKOL_GLCORE33)
  1496. #error("sokol_app.h: unknown 3D API selected for Win32, must be SOKOL_D3D11 or SOKOL_GLCORE33")
  1497. #endif
  1498. #endif
  1499. #elif defined(__ANDROID__)
  1500. /* Android */
  1501. #define _SAPP_ANDROID (1)
  1502. #if !defined(SOKOL_GLES3) && !defined(SOKOL_GLES2)
  1503. #error("sokol_app.h: unknown 3D API selected for Android, must be SOKOL_GLES3 or SOKOL_GLES2")
  1504. #endif
  1505. #if defined(SOKOL_NO_ENTRY)
  1506. #error("sokol_app.h: SOKOL_NO_ENTRY is not supported on Android")
  1507. #endif
  1508. #elif defined(__linux__) || defined(__unix__)
  1509. /* Linux */
  1510. #define _SAPP_LINUX (1)
  1511. #if !defined(SOKOL_GLCORE33)
  1512. #error("sokol_app.h: unknown 3D API selected for Linux, must be SOKOL_GLCORE33")
  1513. #endif
  1514. #else
  1515. #error "sokol_app.h: Unknown platform"
  1516. #endif
  1517. #ifndef SOKOL_API_IMPL
  1518. #define SOKOL_API_IMPL
  1519. #endif
  1520. #ifndef SOKOL_DEBUG
  1521. #ifndef NDEBUG
  1522. #define SOKOL_DEBUG (1)
  1523. #endif
  1524. #endif
  1525. #ifndef SOKOL_ASSERT
  1526. #include <assert.h>
  1527. #define SOKOL_ASSERT(c) assert(c)
  1528. #endif
  1529. #ifndef SOKOL_UNREACHABLE
  1530. #define SOKOL_UNREACHABLE SOKOL_ASSERT(false)
  1531. #endif
  1532. #if !defined(SOKOL_CALLOC) || !defined(SOKOL_FREE)
  1533. #include <stdlib.h>
  1534. #endif
  1535. #if !defined(SOKOL_CALLOC)
  1536. #define SOKOL_CALLOC(n,s) calloc(n,s)
  1537. #endif
  1538. #if !defined(SOKOL_FREE)
  1539. #define SOKOL_FREE(p) free(p)
  1540. #endif
  1541. #ifndef SOKOL_LOG
  1542. #ifdef SOKOL_DEBUG
  1543. #if defined(__ANDROID__)
  1544. #include <android/log.h>
  1545. #define SOKOL_LOG(s) { SOKOL_ASSERT(s); __android_log_write(ANDROID_LOG_INFO, "SOKOL_APP", s); }
  1546. #else
  1547. #include <stdio.h>
  1548. #define SOKOL_LOG(s) { SOKOL_ASSERT(s); puts(s); }
  1549. #endif
  1550. #else
  1551. #define SOKOL_LOG(s)
  1552. #endif
  1553. #endif
  1554. #ifndef SOKOL_ABORT
  1555. #include <stdlib.h>
  1556. #define SOKOL_ABORT() abort()
  1557. #endif
  1558. #ifndef _SOKOL_PRIVATE
  1559. #if defined(__GNUC__) || defined(__clang__)
  1560. #define _SOKOL_PRIVATE __attribute__((unused)) static
  1561. #else
  1562. #define _SOKOL_PRIVATE static
  1563. #endif
  1564. #endif
  1565. #ifndef _SOKOL_UNUSED
  1566. #define _SOKOL_UNUSED(x) (void)(x)
  1567. #endif
  1568. /*== PLATFORM SPECIFIC INCLUDES AND DEFINES ==================================*/
  1569. #if defined(_SAPP_APPLE)
  1570. #if defined(SOKOL_METAL)
  1571. #import <Metal/Metal.h>
  1572. #import <MetalKit/MetalKit.h>
  1573. #endif
  1574. #if defined(_SAPP_MACOS)
  1575. #if !defined(SOKOL_METAL)
  1576. #ifndef GL_SILENCE_DEPRECATION
  1577. #define GL_SILENCE_DEPRECATION
  1578. #endif
  1579. #include <Cocoa/Cocoa.h>
  1580. #endif
  1581. #elif defined(_SAPP_IOS)
  1582. #import <UIKit/UIKit.h>
  1583. #if !defined(SOKOL_METAL)
  1584. #import <GLKit/GLKit.h>
  1585. #endif
  1586. #endif
  1587. #elif defined(_SAPP_EMSCRIPTEN)
  1588. #if defined(SOKOL_WGPU)
  1589. #include <webgpu/webgpu.h>
  1590. #endif
  1591. #include <emscripten/emscripten.h>
  1592. #include <emscripten/html5.h>
  1593. #elif defined(_SAPP_WIN32)
  1594. #ifdef _MSC_VER
  1595. #pragma warning(push)
  1596. #pragma warning(disable:4201) /* nonstandard extension used: nameless struct/union */
  1597. #pragma warning(disable:4204) /* nonstandard extension used: non-constant aggregate initializer */
  1598. #pragma warning(disable:4054) /* 'type cast': from function pointer */
  1599. #pragma warning(disable:4055) /* 'type cast': from data pointer */
  1600. #pragma warning(disable:4505) /* unreferenced local function has been removed */
  1601. #pragma warning(disable:4115) /* /W4: 'ID3D11ModuleInstance': named type definition in parentheses (in d3d11.h) */
  1602. #endif
  1603. #ifndef WIN32_LEAN_AND_MEAN
  1604. #define WIN32_LEAN_AND_MEAN
  1605. #endif
  1606. #ifndef NOMINMAX
  1607. #define NOMINMAX
  1608. #endif
  1609. #include <windows.h>
  1610. #include <windowsx.h>
  1611. #include <shellapi.h>
  1612. #if !defined(SOKOL_NO_ENTRY) // if SOKOL_NO_ENTRY is defined, it's the applications' responsibility to use the right subsystem
  1613. #if defined(SOKOL_WIN32_FORCE_MAIN)
  1614. #pragma comment (linker, "/subsystem:console")
  1615. #else
  1616. #pragma comment (linker, "/subsystem:windows")
  1617. #endif
  1618. #endif
  1619. #include <stdio.h> /* freopen_s() */
  1620. #include <wchar.h> /* wcslen() */
  1621. #pragma comment (lib, "kernel32")
  1622. #pragma comment (lib, "user32")
  1623. #pragma comment (lib, "shell32") /* CommandLineToArgvW, DragQueryFileW, DragFinished */
  1624. #if defined(SOKOL_D3D11)
  1625. #pragma comment (lib, "dxgi")
  1626. #pragma comment (lib, "d3d11")
  1627. #pragma comment (lib, "dxguid")
  1628. #endif
  1629. #if defined(SOKOL_GLCORE33)
  1630. #pragma comment (lib, "gdi32")
  1631. #endif
  1632. #if defined(SOKOL_D3D11)
  1633. #ifndef D3D11_NO_HELPERS
  1634. #define D3D11_NO_HELPERS
  1635. #endif
  1636. #include <d3d11.h>
  1637. #include <dxgi.h>
  1638. // DXGI_SWAP_EFFECT_FLIP_DISCARD is only defined in newer Windows SDKs, so don't depend on it
  1639. #define _SAPP_DXGI_SWAP_EFFECT_FLIP_DISCARD (4)
  1640. #endif
  1641. #ifndef WM_MOUSEHWHEEL /* see https://github.com/floooh/sokol/issues/138 */
  1642. #define WM_MOUSEHWHEEL (0x020E)
  1643. #endif
  1644. #elif defined(_SAPP_UWP)
  1645. #ifndef NOMINMAX
  1646. #define NOMINMAX
  1647. #endif
  1648. #ifdef _MSC_VER
  1649. #pragma warning(push)
  1650. #pragma warning(disable:4201) /* nonstandard extension used: nameless struct/union */
  1651. #pragma warning(disable:4054) /* 'type cast': from function pointer */
  1652. #pragma warning(disable:4055) /* 'type cast': from data pointer */
  1653. #pragma warning(disable:4505) /* unreferenced local function has been removed */
  1654. #pragma warning(disable:4115) /* /W4: 'ID3D11ModuleInstance': named type definition in parentheses (in d3d11.h) */
  1655. #endif
  1656. #include <windows.h>
  1657. #include <winrt/Windows.ApplicationModel.Core.h>
  1658. #include <winrt/Windows.Foundation.h>
  1659. #include <winrt/Windows.Foundation.Collections.h>
  1660. #include <winrt/Windows.Graphics.Display.h>
  1661. #include <winrt/Windows.UI.Core.h>
  1662. #include <winrt/Windows.UI.Composition.h>
  1663. #include <winrt/Windows.UI.Input.h>
  1664. #include <winrt/Windows.UI.ViewManagement.h>
  1665. #include <winrt/Windows.System.h>
  1666. #include <ppltasks.h>
  1667. #include <dxgi1_4.h>
  1668. #include <d3d11_3.h>
  1669. #include <DirectXMath.h>
  1670. #pragma comment (lib, "WindowsApp")
  1671. #pragma comment (lib, "dxguid")
  1672. #elif defined(_SAPP_ANDROID)
  1673. #include <pthread.h>
  1674. #include <unistd.h>
  1675. #include <android/native_activity.h>
  1676. #include <android/looper.h>
  1677. #include <EGL/egl.h>
  1678. #elif defined(_SAPP_LINUX)
  1679. #define GL_GLEXT_PROTOTYPES
  1680. #include <X11/Xlib.h>
  1681. #include <X11/Xutil.h>
  1682. #include <X11/XKBlib.h>
  1683. #include <X11/keysym.h>
  1684. #include <X11/Xresource.h>
  1685. #include <X11/Xatom.h>
  1686. #include <X11/extensions/XInput2.h>
  1687. #include <X11/Xcursor/Xcursor.h>
  1688. #include <X11/Xmd.h> /* CARD32 */
  1689. #include <dlfcn.h> /* dlopen, dlsym, dlclose */
  1690. #include <limits.h> /* LONG_MAX */
  1691. #include <pthread.h> /* only used a linker-guard, search for _sapp_linux_run() and see first comment */
  1692. #endif
  1693. /*== MACOS DECLARATIONS ======================================================*/
  1694. #if defined(_SAPP_MACOS)
  1695. @interface _sapp_macos_app_delegate : NSObject<NSApplicationDelegate>
  1696. - (void)drawFrame;
  1697. @end
  1698. @interface _sapp_macos_window : NSWindow
  1699. @property uint32_t win_id;
  1700. @end
  1701. @interface _sapp_macos_window_delegate : NSObject<NSWindowDelegate>
  1702. @property uint32_t win_id;
  1703. @end
  1704. #if defined(SOKOL_METAL)
  1705. @interface _sapp_macos_view : MTKView
  1706. @property uint32_t win_id;
  1707. @end
  1708. #elif defined(SOKOL_GLCORE33)
  1709. @interface _sapp_macos_view : NSOpenGLView
  1710. @property uint32_t win_id;
  1711. @end
  1712. #endif // SOKOL_GLCORE33
  1713. typedef struct {
  1714. _sapp_macos_window* window;
  1715. _sapp_macos_window_delegate* delegate;
  1716. _sapp_macos_view* view;
  1717. NSTrackingArea* tracking_area;
  1718. uint32_t flags_changed_store;
  1719. uint8_t mouse_buttons;
  1720. } _sapp_macos_window_t;
  1721. typedef struct {
  1722. _sapp_macos_app_delegate* app_delegate;
  1723. #if defined(SOKOL_METAL)
  1724. id<MTLDevice> mtl_device;
  1725. #endif
  1726. dispatch_source_t display_source;
  1727. CVDisplayLinkRef display_link;
  1728. } _sapp_macos_t;
  1729. #endif // _SAPP_MACOS
  1730. /*== IOS DECLARATIONS ========================================================*/
  1731. #if defined(_SAPP_IOS)
  1732. @interface _sapp_app_delegate : NSObject<UIApplicationDelegate>
  1733. @end
  1734. @interface _sapp_textfield_dlg : NSObject<UITextFieldDelegate>
  1735. - (void)keyboardWasShown:(NSNotification*)notif;
  1736. - (void)keyboardWillBeHidden:(NSNotification*)notif;
  1737. - (void)keyboardDidChangeFrame:(NSNotification*)notif;
  1738. @end
  1739. #if defined(SOKOL_METAL)
  1740. @interface _sapp_ios_view : MTKView;
  1741. @end
  1742. #else
  1743. @interface _sapp_ios_view : GLKView
  1744. @end
  1745. #endif
  1746. typedef struct {
  1747. UIWindow* window;
  1748. _sapp_ios_view* view;
  1749. UITextField* textfield;
  1750. _sapp_textfield_dlg* textfield_dlg;
  1751. #if defined(SOKOL_METAL)
  1752. UIViewController* view_ctrl;
  1753. id<MTLDevice> mtl_device;
  1754. #else
  1755. GLKViewController* view_ctrl;
  1756. EAGLContext* eagl_ctx;
  1757. #endif
  1758. bool suspended;
  1759. } _sapp_ios_t;
  1760. #endif // _SAPP_IOS
  1761. /*== EMSCRIPTEN DECLARATIONS =================================================*/
  1762. #if defined(_SAPP_EMSCRIPTEN)
  1763. #if defined(SOKOL_WGPU)
  1764. typedef struct {
  1765. int state;
  1766. WGPUDevice device;
  1767. WGPUSwapChain swapchain;
  1768. WGPUTextureFormat render_format;
  1769. WGPUTexture msaa_tex;
  1770. WGPUTexture depth_stencil_tex;
  1771. WGPUTextureView swapchain_view;
  1772. WGPUTextureView msaa_view;
  1773. WGPUTextureView depth_stencil_view;
  1774. } _sapp_wgpu_t;
  1775. #endif
  1776. typedef struct {
  1777. bool textfield_created;
  1778. bool wants_show_keyboard;
  1779. bool wants_hide_keyboard;
  1780. bool mouse_lock_requested;
  1781. bool ask_leave_site;
  1782. uint16_t mouse_buttons;
  1783. #if defined(SOKOL_WGPU)
  1784. _sapp_wgpu_t wgpu;
  1785. #endif
  1786. char canvas_selector[_SAPP_MAX_TITLE_LENGTH];
  1787. } _sapp_emsc_t;
  1788. #endif // _SAPP_EMSCRIPTEN
  1789. /*== WIN32 DECLARATIONS ======================================================*/
  1790. #if defined(SOKOL_D3D11) && (defined(_SAPP_WIN32) || defined(_SAPP_UWP))
  1791. typedef struct {
  1792. ID3D11Device* device;
  1793. ID3D11DeviceContext* device_context;
  1794. ID3D11Texture2D* rt;
  1795. ID3D11RenderTargetView* rtv;
  1796. ID3D11Texture2D* msaa_rt;
  1797. ID3D11RenderTargetView* msaa_rtv;
  1798. ID3D11Texture2D* ds;
  1799. ID3D11DepthStencilView* dsv;
  1800. DXGI_SWAP_CHAIN_DESC swap_chain_desc;
  1801. IDXGISwapChain* swap_chain;
  1802. } _sapp_d3d11_t;
  1803. #endif
  1804. /*== WIN32 DECLARATIONS ======================================================*/
  1805. #if defined(_SAPP_WIN32)
  1806. #ifndef DPI_ENUMS_DECLARED
  1807. typedef enum PROCESS_DPI_AWARENESS
  1808. {
  1809. PROCESS_DPI_UNAWARE = 0,
  1810. PROCESS_SYSTEM_DPI_AWARE = 1,
  1811. PROCESS_PER_MONITOR_DPI_AWARE = 2
  1812. } PROCESS_DPI_AWARENESS;
  1813. typedef enum MONITOR_DPI_TYPE {
  1814. MDT_EFFECTIVE_DPI = 0,
  1815. MDT_ANGULAR_DPI = 1,
  1816. MDT_RAW_DPI = 2,
  1817. MDT_DEFAULT = MDT_EFFECTIVE_DPI
  1818. } MONITOR_DPI_TYPE;
  1819. #endif /*DPI_ENUMS_DECLARED*/
  1820. typedef struct {
  1821. bool aware;
  1822. float content_scale;
  1823. float window_scale;
  1824. float mouse_scale;
  1825. } _sapp_win32_dpi_t;
  1826. typedef struct {
  1827. HWND hwnd;
  1828. HDC dc;
  1829. HICON big_icon;
  1830. HICON small_icon;
  1831. UINT orig_codepage;
  1832. LONG mouse_locked_x, mouse_locked_y;
  1833. bool is_win10_or_greater;
  1834. bool in_create_window;
  1835. bool iconified;
  1836. bool mouse_tracked;
  1837. uint8_t mouse_capture_mask;
  1838. _sapp_win32_dpi_t dpi;
  1839. bool raw_input_mousepos_valid;
  1840. LONG raw_input_mousepos_x;
  1841. LONG raw_input_mousepos_y;
  1842. uint8_t raw_input_data[256];
  1843. } _sapp_win32_t;
  1844. #if defined(SOKOL_GLCORE33)
  1845. #define WGL_NUMBER_PIXEL_FORMATS_ARB 0x2000
  1846. #define WGL_SUPPORT_OPENGL_ARB 0x2010
  1847. #define WGL_DRAW_TO_WINDOW_ARB 0x2001
  1848. #define WGL_PIXEL_TYPE_ARB 0x2013
  1849. #define WGL_TYPE_RGBA_ARB 0x202b
  1850. #define WGL_ACCELERATION_ARB 0x2003
  1851. #define WGL_NO_ACCELERATION_ARB 0x2025
  1852. #define WGL_RED_BITS_ARB 0x2015
  1853. #define WGL_GREEN_BITS_ARB 0x2017
  1854. #define WGL_BLUE_BITS_ARB 0x2019
  1855. #define WGL_ALPHA_BITS_ARB 0x201b
  1856. #define WGL_DEPTH_BITS_ARB 0x2022
  1857. #define WGL_STENCIL_BITS_ARB 0x2023
  1858. #define WGL_DOUBLE_BUFFER_ARB 0x2011
  1859. #define WGL_SAMPLES_ARB 0x2042
  1860. #define WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB 0x00000002
  1861. #define WGL_CONTEXT_PROFILE_MASK_ARB 0x9126
  1862. #define WGL_CONTEXT_CORE_PROFILE_BIT_ARB 0x00000001
  1863. #define WGL_CONTEXT_MAJOR_VERSION_ARB 0x2091
  1864. #define WGL_CONTEXT_MINOR_VERSION_ARB 0x2092
  1865. #define WGL_CONTEXT_FLAGS_ARB 0x2094
  1866. #define ERROR_INVALID_VERSION_ARB 0x2095
  1867. #define ERROR_INVALID_PROFILE_ARB 0x2096
  1868. #define ERROR_INCOMPATIBLE_DEVICE_CONTEXTS_ARB 0x2054
  1869. typedef BOOL (WINAPI * PFNWGLSWAPINTERVALEXTPROC)(int);
  1870. typedef BOOL (WINAPI * PFNWGLGETPIXELFORMATATTRIBIVARBPROC)(HDC,int,int,UINT,const int*,int*);
  1871. typedef const char* (WINAPI * PFNWGLGETEXTENSIONSSTRINGEXTPROC)(void);
  1872. typedef const char* (WINAPI * PFNWGLGETEXTENSIONSSTRINGARBPROC)(HDC);
  1873. typedef HGLRC (WINAPI * PFNWGLCREATECONTEXTATTRIBSARBPROC)(HDC,HGLRC,const int*);
  1874. typedef HGLRC (WINAPI * PFN_wglCreateContext)(HDC);
  1875. typedef BOOL (WINAPI * PFN_wglDeleteContext)(HGLRC);
  1876. typedef PROC (WINAPI * PFN_wglGetProcAddress)(LPCSTR);
  1877. typedef HDC (WINAPI * PFN_wglGetCurrentDC)(void);
  1878. typedef BOOL (WINAPI * PFN_wglMakeCurrent)(HDC,HGLRC);
  1879. typedef struct {
  1880. HINSTANCE opengl32;
  1881. HGLRC gl_ctx;
  1882. PFN_wglCreateContext CreateContext;
  1883. PFN_wglDeleteContext DeleteContext;
  1884. PFN_wglGetProcAddress GetProcAddress;
  1885. PFN_wglGetCurrentDC GetCurrentDC;
  1886. PFN_wglMakeCurrent MakeCurrent;
  1887. PFNWGLSWAPINTERVALEXTPROC SwapIntervalEXT;
  1888. PFNWGLGETPIXELFORMATATTRIBIVARBPROC GetPixelFormatAttribivARB;
  1889. PFNWGLGETEXTENSIONSSTRINGEXTPROC GetExtensionsStringEXT;
  1890. PFNWGLGETEXTENSIONSSTRINGARBPROC GetExtensionsStringARB;
  1891. PFNWGLCREATECONTEXTATTRIBSARBPROC CreateContextAttribsARB;
  1892. bool ext_swap_control;
  1893. bool arb_multisample;
  1894. bool arb_pixel_format;
  1895. bool arb_create_context;
  1896. bool arb_create_context_profile;
  1897. HWND msg_hwnd;
  1898. HDC msg_dc;
  1899. } _sapp_wgl_t;
  1900. #endif // SOKOL_GLCORE33
  1901. #endif // _SAPP_WIN32
  1902. /*== UWP DECLARATIONS ======================================================*/
  1903. #if defined(_SAPP_UWP)
  1904. typedef struct {
  1905. float content_scale;
  1906. float window_scale;
  1907. float mouse_scale;
  1908. } _sapp_uwp_dpi_t;
  1909. typedef struct {
  1910. bool mouse_tracked;
  1911. uint8_t mouse_buttons;
  1912. _sapp_uwp_dpi_t dpi;
  1913. } _sapp_uwp_t;
  1914. #endif // _SAPP_UWP
  1915. /*== ANDROID DECLARATIONS ====================================================*/
  1916. #if defined(_SAPP_ANDROID)
  1917. typedef enum {
  1918. _SOKOL_ANDROID_MSG_CREATE,
  1919. _SOKOL_ANDROID_MSG_RESUME,
  1920. _SOKOL_ANDROID_MSG_PAUSE,
  1921. _SOKOL_ANDROID_MSG_FOCUS,
  1922. _SOKOL_ANDROID_MSG_NO_FOCUS,
  1923. _SOKOL_ANDROID_MSG_SET_NATIVE_WINDOW,
  1924. _SOKOL_ANDROID_MSG_SET_INPUT_QUEUE,
  1925. _SOKOL_ANDROID_MSG_DESTROY,
  1926. } _sapp_android_msg_t;
  1927. typedef struct {
  1928. pthread_t thread;
  1929. pthread_mutex_t mutex;
  1930. pthread_cond_t cond;
  1931. int read_from_main_fd;
  1932. int write_from_main_fd;
  1933. } _sapp_android_pt_t;
  1934. typedef struct {
  1935. ANativeWindow* window;
  1936. AInputQueue* input;
  1937. } _sapp_android_resources_t;
  1938. typedef struct {
  1939. ANativeActivity* activity;
  1940. _sapp_android_pt_t pt;
  1941. _sapp_android_resources_t pending;
  1942. _sapp_android_resources_t current;
  1943. ALooper* looper;
  1944. bool is_thread_started;
  1945. bool is_thread_stopping;
  1946. bool is_thread_stopped;
  1947. bool has_created;
  1948. bool has_resumed;
  1949. bool has_focus;
  1950. EGLConfig config;
  1951. EGLDisplay display;
  1952. EGLContext context;
  1953. EGLSurface surface;
  1954. } _sapp_android_t;
  1955. #endif // _SAPP_ANDROID
  1956. /*== LINUX DECLARATIONS ======================================================*/
  1957. #if defined(_SAPP_LINUX)
  1958. #define _SAPP_X11_XDND_VERSION (5)
  1959. #define GLX_VENDOR 1
  1960. #define GLX_RGBA_BIT 0x00000001
  1961. #define GLX_WINDOW_BIT 0x00000001
  1962. #define GLX_DRAWABLE_TYPE 0x8010
  1963. #define GLX_RENDER_TYPE 0x8011
  1964. #define GLX_DOUBLEBUFFER 5
  1965. #define GLX_RED_SIZE 8
  1966. #define GLX_GREEN_SIZE 9
  1967. #define GLX_BLUE_SIZE 10
  1968. #define GLX_ALPHA_SIZE 11
  1969. #define GLX_DEPTH_SIZE 12
  1970. #define GLX_STENCIL_SIZE 13
  1971. #define GLX_SAMPLES 0x186a1
  1972. #define GLX_CONTEXT_CORE_PROFILE_BIT_ARB 0x00000001
  1973. #define GLX_CONTEXT_PROFILE_MASK_ARB 0x9126
  1974. #define GLX_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB 0x00000002
  1975. #define GLX_CONTEXT_MAJOR_VERSION_ARB 0x2091
  1976. #define GLX_CONTEXT_MINOR_VERSION_ARB 0x2092
  1977. #define GLX_CONTEXT_FLAGS_ARB 0x2094
  1978. typedef XID GLXWindow;
  1979. typedef XID GLXDrawable;
  1980. typedef struct __GLXFBConfig* GLXFBConfig;
  1981. typedef struct __GLXcontext* GLXContext;
  1982. typedef void (*__GLXextproc)(void);
  1983. typedef int (*PFNGLXGETFBCONFIGATTRIBPROC)(Display*,GLXFBConfig,int,int*);
  1984. typedef const char* (*PFNGLXGETCLIENTSTRINGPROC)(Display*,int);
  1985. typedef Bool (*PFNGLXQUERYEXTENSIONPROC)(Display*,int*,int*);
  1986. typedef Bool (*PFNGLXQUERYVERSIONPROC)(Display*,int*,int*);
  1987. typedef void (*PFNGLXDESTROYCONTEXTPROC)(Display*,GLXContext);
  1988. typedef Bool (*PFNGLXMAKECURRENTPROC)(Display*,GLXDrawable,GLXContext);
  1989. typedef void (*PFNGLXSWAPBUFFERSPROC)(Display*,GLXDrawable);
  1990. typedef const char* (*PFNGLXQUERYEXTENSIONSSTRINGPROC)(Display*,int);
  1991. typedef GLXFBConfig* (*PFNGLXGETFBCONFIGSPROC)(Display*,int,int*);
  1992. typedef __GLXextproc (* PFNGLXGETPROCADDRESSPROC)(const char *procName);
  1993. typedef void (*PFNGLXSWAPINTERVALEXTPROC)(Display*,GLXDrawable,int);
  1994. typedef XVisualInfo* (*PFNGLXGETVISUALFROMFBCONFIGPROC)(Display*,GLXFBConfig);
  1995. typedef GLXWindow (*PFNGLXCREATEWINDOWPROC)(Display*,GLXFBConfig,Window,const int*);
  1996. typedef void (*PFNGLXDESTROYWINDOWPROC)(Display*,GLXWindow);
  1997. typedef int (*PFNGLXSWAPINTERVALMESAPROC)(int);
  1998. typedef GLXContext (*PFNGLXCREATECONTEXTATTRIBSARBPROC)(Display*,GLXFBConfig,GLXContext,Bool,const int*);
  1999. typedef struct {
  2000. bool available;
  2001. int major_opcode;
  2002. int event_base;
  2003. int error_base;
  2004. int major;
  2005. int minor;
  2006. } _sapp_xi_t;
  2007. typedef struct {
  2008. int version;
  2009. Window source;
  2010. Atom format;
  2011. Atom XdndAware;
  2012. Atom XdndEnter;
  2013. Atom XdndPosition;
  2014. Atom XdndStatus;
  2015. Atom XdndActionCopy;
  2016. Atom XdndDrop;
  2017. Atom XdndFinished;
  2018. Atom XdndSelection;
  2019. Atom XdndTypeList;
  2020. Atom text_uri_list;
  2021. } _sapp_xdnd_t;
  2022. typedef struct {
  2023. uint8_t mouse_buttons;
  2024. Display* display;
  2025. int screen;
  2026. Window root;
  2027. Colormap colormap;
  2028. Window window;
  2029. Cursor hidden_cursor;
  2030. int window_state;
  2031. float dpi;
  2032. unsigned char error_code;
  2033. Atom UTF8_STRING;
  2034. Atom WM_PROTOCOLS;
  2035. Atom WM_DELETE_WINDOW;
  2036. Atom WM_STATE;
  2037. Atom NET_WM_NAME;
  2038. Atom NET_WM_ICON_NAME;
  2039. Atom NET_WM_ICON;
  2040. Atom NET_WM_STATE;
  2041. Atom NET_WM_STATE_FULLSCREEN;
  2042. _sapp_xi_t xi;
  2043. _sapp_xdnd_t xdnd;
  2044. } _sapp_x11_t;
  2045. typedef struct {
  2046. void* libgl;
  2047. int major;
  2048. int minor;
  2049. int event_base;
  2050. int error_base;
  2051. GLXContext ctx;
  2052. GLXWindow window;
  2053. // GLX 1.3 functions
  2054. PFNGLXGETFBCONFIGSPROC GetFBConfigs;
  2055. PFNGLXGETFBCONFIGATTRIBPROC GetFBConfigAttrib;
  2056. PFNGLXGETCLIENTSTRINGPROC GetClientString;
  2057. PFNGLXQUERYEXTENSIONPROC QueryExtension;
  2058. PFNGLXQUERYVERSIONPROC QueryVersion;
  2059. PFNGLXDESTROYCONTEXTPROC DestroyContext;
  2060. PFNGLXMAKECURRENTPROC MakeCurrent;
  2061. PFNGLXSWAPBUFFERSPROC SwapBuffers;
  2062. PFNGLXQUERYEXTENSIONSSTRINGPROC QueryExtensionsString;
  2063. PFNGLXGETVISUALFROMFBCONFIGPROC GetVisualFromFBConfig;
  2064. PFNGLXCREATEWINDOWPROC CreateWindow;
  2065. PFNGLXDESTROYWINDOWPROC DestroyWindow;
  2066. // GLX 1.4 and extension functions
  2067. PFNGLXGETPROCADDRESSPROC GetProcAddress;
  2068. PFNGLXGETPROCADDRESSPROC GetProcAddressARB;
  2069. PFNGLXSWAPINTERVALEXTPROC SwapIntervalEXT;
  2070. PFNGLXSWAPINTERVALMESAPROC SwapIntervalMESA;
  2071. PFNGLXCREATECONTEXTATTRIBSARBPROC CreateContextAttribsARB;
  2072. // extension availability
  2073. bool EXT_swap_control;
  2074. bool MESA_swap_control;
  2075. bool ARB_multisample;
  2076. bool ARB_create_context;
  2077. bool ARB_create_context_profile;
  2078. } _sapp_glx_t;
  2079. #endif // _SAPP_LINUX
  2080. /*== COMMON DECLARATIONS =====================================================*/
  2081. /* helper macros */
  2082. #define _sapp_def(val, def) (((val) == 0) ? (def) : (val))
  2083. #define _sapp_absf(a) (((a)<0.0f)?-(a):(a))
  2084. #define _SAPP_MAX_TITLE_LENGTH (128)
  2085. /* NOTE: the pixel format values *must* be compatible with sg_pixel_format */
  2086. #define _SAPP_PIXELFORMAT_RGBA8 (23)
  2087. #define _SAPP_PIXELFORMAT_BGRA8 (27)
  2088. #define _SAPP_PIXELFORMAT_DEPTH (41)
  2089. #define _SAPP_PIXELFORMAT_DEPTH_STENCIL (42)
  2090. /* this MUST be 0 */
  2091. #define _SAPP_INVALID_SLOT_INDEX (0)
  2092. #define _SAPP_SLOT_SHIFT (16)
  2093. #define _SAPP_SLOT_MASK ((1<<_SAPP_SLOT_SHIFT)-1)
  2094. #define _SAPP_MAX_POOL_SIZE (1<<_SAPP_SLOT_SHIFT)
  2095. #define _SAPP_DEFAULT_POOL_SIZE (128)
  2096. #if defined(_SAPP_MACOS) || defined(_SAPP_IOS)
  2097. // this is ARC compatible
  2098. #if defined(__cplusplus)
  2099. #define _SAPP_CLEAR_ITEM(type, item) { item = (type) { }; }
  2100. #define _SAPP_CLEAR_PTR(type, ptr) { *ptr = (type) { }; }
  2101. #else
  2102. #define _SAPP_CLEAR_ITEM(type, item) { item = (type) { 0 }; }
  2103. #define _SAPP_CLEAR_PTR(type, ptr) { *ptr = (type) { 0 }; }
  2104. #endif
  2105. #else
  2106. #define _SAPP_CLEAR_ITEM(type, item) { memset(&item, 0, sizeof(item)); }
  2107. #define _SAPP_CLEAR_PTR(type, ptr) { memset(ptr, 0, sizeof(type)); }
  2108. #endif
  2109. typedef struct {
  2110. int size;
  2111. int queue_top;
  2112. uint32_t* gen_ctrs;
  2113. int* free_queue;
  2114. } _sapp_pool_t;
  2115. typedef struct {
  2116. uint32_t id;
  2117. } _sapp_slot_t;
  2118. typedef struct {
  2119. bool enabled;
  2120. int buf_size;
  2121. char* buffer;
  2122. } _sapp_clipboard_t;
  2123. typedef struct {
  2124. bool enabled;
  2125. int max_files;
  2126. int max_path_length;
  2127. int num_files;
  2128. int buf_size;
  2129. char* buffer;
  2130. } _sapp_drop_t;
  2131. typedef struct {
  2132. float x, y;
  2133. float dx, dy;
  2134. bool shown;
  2135. bool locked;
  2136. bool pos_valid;
  2137. } _sapp_mouse_t;
  2138. typedef struct {
  2139. _sapp_slot_t slot;
  2140. sapp_window_desc desc;
  2141. int pos_x;
  2142. int pos_y;
  2143. int window_width;
  2144. int window_height;
  2145. int framebuffer_width;
  2146. int framebuffer_height;
  2147. float dpi_scale;
  2148. bool fullscreen;
  2149. _sapp_mouse_t mouse;
  2150. char title[_SAPP_MAX_TITLE_LENGTH];
  2151. #if defined(_SAPP_MACOS)
  2152. _sapp_macos_window_t macos;
  2153. #elif defined(_SAPP_IOS)
  2154. _sapp_ios_window_t ios;
  2155. #elif defined(_SAPP_EMSCRIPTEN)
  2156. _sapp_emsc_window_t emsc;
  2157. #elif defined(_SAPP_WIN32)
  2158. _sapp_win32_window_t win32;
  2159. #if defined(SOKOL_D3D11)
  2160. _sapp_d3d11_window_t d3d11;
  2161. #elif defined(SOKOL_GLCORE33)
  2162. _sapp_wgl_window_t wgl;
  2163. #endif
  2164. #elif defined(_SAPP_UWP)
  2165. _sapp_uwp_window_t uwp;
  2166. #if defined(SOKOL_D3D11)
  2167. _sapp_d3d11_window_t d3d11;
  2168. #endif
  2169. #elif defined(_SAPP_ANDROID)
  2170. _sapp_android_window_t android;
  2171. #elif defined(_SAPP_LINUX)
  2172. _sapp_x11_window_t x11;
  2173. _sapp_glx_window_t glx;
  2174. #endif
  2175. } _sapp_window_t;
  2176. typedef struct {
  2177. _sapp_pool_t pool;
  2178. _sapp_window_t* windows;
  2179. } _sapp_window_pool_t;
  2180. typedef struct {
  2181. sapp_desc desc;
  2182. _sapp_window_pool_t window_pool;
  2183. uint32_t main_window_id;
  2184. bool valid;
  2185. bool first_frame;
  2186. bool init_called;
  2187. bool cleanup_called;
  2188. bool event_consumed;
  2189. bool gles2_fallback;
  2190. bool quit_requested;
  2191. bool quit_ordered;
  2192. bool onscreen_keyboard_shown;
  2193. uint64_t frame_count;
  2194. sapp_event event;
  2195. sapp_icon_desc default_icon_desc;
  2196. uint32_t* default_icon_pixels;
  2197. _sapp_clipboard_t clipboard;
  2198. _sapp_drop_t drop;
  2199. #if defined(_SAPP_MACOS)
  2200. _sapp_macos_t macos;
  2201. #elif defined(_SAPP_IOS)
  2202. _sapp_ios_t ios;
  2203. #elif defined(_SAPP_EMSCRIPTEN)
  2204. _sapp_emsc_t emsc;
  2205. #elif defined(_SAPP_WIN32)
  2206. _sapp_win32_t win32;
  2207. #if defined(SOKOL_D3D11)
  2208. _sapp_d3d11_t d3d11;
  2209. #elif defined(SOKOL_GLCORE33)
  2210. _sapp_wgl_t wgl;
  2211. #endif
  2212. #elif defined(_SAPP_UWP)
  2213. _sapp_uwp_t uwp;
  2214. #if defined(SOKOL_D3D11)
  2215. _sapp_d3d11_t d3d11;
  2216. #endif
  2217. #elif defined(_SAPP_ANDROID)
  2218. _sapp_android_t android;
  2219. #elif defined(_SAPP_LINUX)
  2220. _sapp_x11_t x11;
  2221. _sapp_glx_t glx;
  2222. #endif
  2223. sapp_keycode keycodes[SAPP_MAX_KEYCODES];
  2224. char desc_window_title[_SAPP_MAX_TITLE_LENGTH];
  2225. } _sapp_t;
  2226. static _sapp_t _sapp;
  2227. // forward-declared platform-specific functions
  2228. #if defined(_SAPP_MACOS)
  2229. _SOKOL_PRIVATE void _sapp_macos_init_state(void);
  2230. _SOKOL_PRIVATE void _sapp_macos_discard_state(void);
  2231. _SOKOL_PRIVATE bool _sapp_macos_create_window(_sapp_window_t* win);
  2232. _SOKOL_PRIVATE void _sapp_macos_destroy_window(_sapp_window_t* win);
  2233. _SOKOL_PRIVATE void _sapp_macos_close_window(_sapp_window_t* win);
  2234. _SOKOL_PRIVATE void _sapp_macos_show_window(_sapp_window_t* win);
  2235. _SOKOL_PRIVATE void _sapp_macos_hide_window(_sapp_window_t* win);
  2236. _SOKOL_PRIVATE bool _sapp_macos_window_visible(_sapp_window_t* win);
  2237. _SOKOL_PRIVATE void _sapp_macos_focus_window(_sapp_window_t* win);
  2238. _SOKOL_PRIVATE bool _sapp_macos_window_focused(_sapp_window_t* win);
  2239. _SOKOL_PRIVATE bool _sapp_macos_window_minimized(_sapp_window_t* win);
  2240. _SOKOL_PRIVATE void _sapp_macos_set_window_pos(_sapp_window_t* win, int x, int y);
  2241. _SOKOL_PRIVATE void _sapp_macos_get_window_pos(_sapp_window_t* win, int* x, int* y);
  2242. _SOKOL_PRIVATE void _sapp_macos_set_window_size(_sapp_window_t* win, int w, int h);
  2243. _SOKOL_PRIVATE void _sapp_macos_get_window_size(_sapp_window_t* win, int* w, int* h);
  2244. _SOKOL_PRIVATE void _sapp_macos_set_icon(const sapp_icon_desc* desc, int num_images);
  2245. _SOKOL_PRIVATE void _sapp_macos_activate_window_context(_sapp_window_t* win);
  2246. #elif defined(_SAPP_IOS)
  2247. _SOKOL_PRIVATE void _sapp_ios_init_state(void);
  2248. _SOKOL_PRIVATE void _sapp_ios_discard_state(void);
  2249. _SOKOL_PRIVATE bool _sapp_ios_create_window(_sapp_window_t* win);
  2250. _SOKOL_PRIVATE void _sapp_ios_destroy_window(_sapp_window_t* win);
  2251. _SOKOL_PRIVATE void _sapp_ios_close_window(_sapp_window_t* win);
  2252. _SOKOL_PRIVATE void _sapp_ios_set_window_pos(_sapp_window_t* win, int x, int y);
  2253. _SOKOL_PRIVATE void _sapp_ios_get_window_pos(_sapp_window_t* win, int* x, int* y);
  2254. _SOKOL_PRIVATE void _sapp_ios_set_window_size(_sapp_window_t* win, int w, int h);
  2255. _SOKOL_PRIVATE void _sapp_ios_get_window_size(_sapp_window_t* win, int* w, int* h);
  2256. #elif defined(_SAPP_EMSCRIPTEN)
  2257. _SOKOL_PRIVATE void _sapp_emsc_init_state(void);
  2258. _SOKOL_PRIVATE void _sapp_emsc_discard_state(void);
  2259. _SOKOL_PRIVATE bool _sapp_emsc_create_window(_sapp_window_t* win);
  2260. _SOKOL_PRIVATE void _sapp_emsc_destroy_window(_sapp_window_t* win);
  2261. _SOKOL_PRIVATE void _sapp_emsc_close_window(_sapp_window_t* win);
  2262. _SOKOL_PRIVATE void _sapp_emsc_set_window_pos(_sapp_window_t* win, int x, int y);
  2263. _SOKOL_PRIVATE void _sapp_emsc_get_window_pos(_sapp_window_t* win, int* x, int* y);
  2264. _SOKOL_PRIVATE void _sapp_emsc_set_window_size(_sapp_window_t* win, int w, int h);
  2265. _SOKOL_PRIVATE void _sapp_emsc_get_window_size(_sapp_window_t* win, int* w, int* h);
  2266. _SOKOL_PRIVATE void _sapp_emsc_set_icon(const sapp_icon_desc* desc, int num_images);
  2267. #elif defined(_SAPP_WIN32)
  2268. _SOKOL_PRIVATE void _sapp_win32_init_state(void);
  2269. _SOKOL_PRIVATE void _sapp_win32_discard_state(void);
  2270. _SOKOL_PRIVATE bool _sapp_win32_create_window(_sapp_window_t* win);
  2271. _SOKOL_PRIVATE void _sapp_win32_destroy_window(_sapp_window_t* win);
  2272. _SOKOL_PRIVATE void _sapp_win32_close_window(_sapp_window_t* win);
  2273. _SOKOL_PRIVATE void _sapp_win32_show_window(_sapp_window_t* win);
  2274. _SOKOL_PRIVATE void _sapp_win32_hide_window(_sapp_window_t* win);
  2275. _SOKOL_PRIVATE bool _sapp_win32_window_visible(_sapp_window_t* win);
  2276. _SOKOL_PRIVATE void _sapp_win32_focus_window(_sapp_window_t* win);
  2277. _SOKOL_PRIVATE bool _sapp_win32_window_focused(_sapp_window_t* win);
  2278. _SOKOL_PRIVATE bool _sapp_win32_window_minimized(_sapp_window_t* win);
  2279. _SOKOL_PRIVATE void _sapp_win32_set_window_pos(_sapp_window_t* win, int x, int y);
  2280. _SOKOL_PRIVATE void _sapp_win32_get_window_pos(_sapp_window_t* win, int* x, int* y);
  2281. _SOKOL_PRIVATE void _sapp_win32_set_window_size(_sapp_window_t* win, int w, int h);
  2282. _SOKOL_PRIVATE void _sapp_win32_get_window_size(_sapp_window_t* win, int* w, int* h);
  2283. _SOKOL_PRIVATE void _sapp_win32_set_icon(const sapp_icon_desc* desc, int num_images);
  2284. _SOKOL_PRIVATE void _sapp_win32_activate_window_context(_sapp_window_t* win);
  2285. #elif defined(_SAPP_UWP)
  2286. _SOKOL_PRIVATE void _sapp_uwp_init_state(void);
  2287. _SOKOL_PRIVATE void _sapp_uwp_discard_state(void);
  2288. _SOKOL_PRIVATE bool _sapp_uwp_create_window(_sapp_window_t* win);
  2289. _SOKOL_PRIVATE void _sapp_uwp_destroy_window(_sapp_window_t* win);
  2290. _SOKOL_PRIVATE void _sapp_uwp_close_window(_sapp_window_t* win);
  2291. _SOKOL_PRIVATE void _sapp_uwp_set_window_pos(_sapp_window_t* win, int x, int y);
  2292. _SOKOL_PRIVATE void _sapp_uwp_get_window_pos(_sapp_window_t* win, int* x, int* y);
  2293. _SOKOL_PRIVATE void _sapp_uwp_set_window_size(_sapp_window_t* win, int w, int h);
  2294. _SOKOL_PRIVATE void _sapp_uwp_get_window_size(_sapp_window_t* win, int* w, int* h);
  2295. #elif defined(_SAPP_ANDROID)
  2296. _SOKOL_PRIVATE void _sapp_android_init_state(void);
  2297. _SOKOL_PRIVATE void _sapp_android_discard_state(void);
  2298. _SOKOL_PRIVATE bool _sapp_android_create_window(_sapp_window_t* win);
  2299. _SOKOL_PRIVATE void _sapp_android_destroy_window(_sapp_window_t* win);
  2300. _SOKOL_PRIVATE void _sapp_android_close_window(_sapp_window_t* win);
  2301. _SOKOL_PRIVATE void _sapp_android_set_window_pos(_sapp_window_t* win, int x, int y);
  2302. _SOKOL_PRIVATE void _sapp_android_get_window_pos(_sapp_window_t* win, int* x, int* y);
  2303. _SOKOL_PRIVATE void _sapp_android_set_window_size(_sapp_window_t* win, int w, int h);
  2304. _SOKOL_PRIVATE void _sapp_android_get_window_size(_sapp_window_t* win, int* w, int* h);
  2305. #elif defined(_SAPP_LINUX)
  2306. _SOKOL_PRIVATE void _sapp_x11_init_state(void);
  2307. _SOKOL_PRIVATE void _sapp_x11_discard_state(void);
  2308. _SOKOL_PRIVATE bool _sapp_x11_create_window(_sapp_window_t* win);
  2309. _SOKOL_PRIVATE void _sapp_x11_destroy_window(_sapp_window_t* win);
  2310. _SOKOL_PRIVATE void _sapp_x11_close_window(_sapp_window_t* win);
  2311. _SOKOL_PRIVATE void _sapp_x11_show_window(_sapp_window_t* win);
  2312. _SOKOL_PRIVATE void _sapp_x11_hide_window(_sapp_window_t* win);
  2313. _SOKOL_PRIVATE bool _sapp_x11_window_visible(_sapp_window_t* win);
  2314. _SOKOL_PRIVATE void _sapp_x11_focus_window(_sapp_window_t* win);
  2315. _SOKOL_PRIVATE bool _sapp_x11_window_focused(_sapp_window_t* win);
  2316. _SOKOL_PRIVATE bool _sapp_x11_window_minimized(_sapp_window_t* win);
  2317. _SOKOL_PRIVATE void _sapp_x11_set_window_pos(_sapp_window_t* win, int x, int y);
  2318. _SOKOL_PRIVATE void _sapp_x11_get_window_pos(_sapp_window_t* win, int* x, int* y);
  2319. _SOKOL_PRIVATE void _sapp_x11_set_window_size(_sapp_window_t* win, int w, int h);
  2320. _SOKOL_PRIVATE void _sapp_x11_get_window_size(_sapp_window_t* win, int* w, int* h);
  2321. _SOKOL_PRIVATE void _sapp_x11_set_icon(const sapp_icon_desc* desc, int num_images);
  2322. _SOKOL_PRIVATE void _sapp_x11_activate_window_context(_sapp_window_t* win);
  2323. #endif
  2324. /*=== POOL IMPLEMENTATION ====================================================*/
  2325. _SOKOL_PRIVATE void _sapp_init_pool(_sapp_pool_t* pool, int num) {
  2326. SOKOL_ASSERT(pool && (num >= 1));
  2327. /* slot 0 is reserved for the 'invalid id', so bump the pool size by 1 */
  2328. pool->size = num + 1;
  2329. pool->queue_top = 0;
  2330. /* generation counters indexable by pool slot index, slot 0 is reserved */
  2331. pool->gen_ctrs = (uint32_t*) SOKOL_CALLOC((size_t)pool->size, sizeof(uint32_t));
  2332. SOKOL_ASSERT(pool->gen_ctrs);
  2333. /* it's not a bug to only reserve 'num' here */
  2334. pool->free_queue = (int*) SOKOL_CALLOC((size_t)num, sizeof(int));
  2335. SOKOL_ASSERT(pool->free_queue);
  2336. /* never allocate the zero-th pool item since the invalid id is 0 */
  2337. for (int i = pool->size-1; i >= 1; i--) {
  2338. pool->free_queue[pool->queue_top++] = i;
  2339. }
  2340. }
  2341. _SOKOL_PRIVATE void _sapp_discard_pool(_sapp_pool_t* pool) {
  2342. SOKOL_ASSERT(pool);
  2343. SOKOL_ASSERT(pool->free_queue);
  2344. SOKOL_FREE(pool->free_queue);
  2345. pool->free_queue = 0;
  2346. SOKOL_ASSERT(pool->gen_ctrs);
  2347. SOKOL_FREE(pool->gen_ctrs);
  2348. pool->gen_ctrs = 0;
  2349. pool->size = 0;
  2350. pool->queue_top = 0;
  2351. }
  2352. _SOKOL_PRIVATE int _sapp_pool_alloc_index(_sapp_pool_t* pool) {
  2353. SOKOL_ASSERT(pool);
  2354. SOKOL_ASSERT(pool->free_queue);
  2355. if (pool->queue_top > 0) {
  2356. int slot_index = pool->free_queue[--pool->queue_top];
  2357. SOKOL_ASSERT((slot_index > 0) && (slot_index < pool->size));
  2358. return slot_index;
  2359. }
  2360. else {
  2361. /* pool exhausted */
  2362. return _SAPP_INVALID_SLOT_INDEX;
  2363. }
  2364. }
  2365. _SOKOL_PRIVATE void _sapp_pool_free_index(_sapp_pool_t* pool, int slot_index) {
  2366. SOKOL_ASSERT((slot_index > _SAPP_INVALID_SLOT_INDEX) && (slot_index < pool->size));
  2367. SOKOL_ASSERT(pool);
  2368. SOKOL_ASSERT(pool->free_queue);
  2369. SOKOL_ASSERT(pool->queue_top < pool->size);
  2370. #ifdef SOKOL_DEBUG
  2371. /* debug check against double-free */
  2372. for (int i = 0; i < pool->queue_top; i++) {
  2373. SOKOL_ASSERT(pool->free_queue[i] != slot_index);
  2374. }
  2375. #endif
  2376. pool->free_queue[pool->queue_top++] = slot_index;
  2377. SOKOL_ASSERT(pool->queue_top <= (pool->size-1));
  2378. }
  2379. /* allocate the slot at slot_index:
  2380. - bump the slot's generation counter
  2381. - create a resource id from the generation counter and slot index
  2382. - set the slot's id to this id
  2383. - set the slot's state to ALLOC
  2384. - return the resource id
  2385. */
  2386. _SOKOL_PRIVATE uint32_t _sapp_slot_alloc(_sapp_pool_t* pool, _sapp_slot_t* slot, int slot_index) {
  2387. SOKOL_ASSERT(pool && pool->gen_ctrs);
  2388. SOKOL_ASSERT((slot_index > _SAPP_INVALID_SLOT_INDEX) && (slot_index < pool->size));
  2389. SOKOL_ASSERT(slot->id == SAPP_INVALID_ID);
  2390. uint32_t ctr = ++pool->gen_ctrs[slot_index];
  2391. slot->id = (ctr<<_SAPP_SLOT_SHIFT)|(slot_index & _SAPP_SLOT_MASK);
  2392. return slot->id;
  2393. }
  2394. _SOKOL_PRIVATE int _sapp_slot_index(uint32_t id) {
  2395. int slot_index = (int) (id & _SAPP_SLOT_MASK);
  2396. SOKOL_ASSERT(_SAPP_INVALID_SLOT_INDEX != slot_index);
  2397. return slot_index;
  2398. }
  2399. _SOKOL_PRIVATE void _sapp_setup_window_pool(const sapp_desc* desc) {
  2400. SOKOL_ASSERT((desc->window_pool_size > 0) && (desc->window_pool_size < _SAPP_MAX_POOL_SIZE));
  2401. _sapp_init_pool(&_sapp.window_pool.pool, desc->window_pool_size);
  2402. _sapp.window_pool.windows = (_sapp_window_t*) SOKOL_CALLOC((size_t)_sapp.window_pool.pool.size, sizeof(_sapp_window_t));
  2403. SOKOL_ASSERT(_sapp.window_pool.windows);
  2404. }
  2405. _SOKOL_PRIVATE void _sapp_discard_window_pool(void) {
  2406. SOKOL_ASSERT(_sapp.window_pool.windows);
  2407. SOKOL_FREE(_sapp.window_pool.windows); _sapp.window_pool.windows = 0;
  2408. _sapp_discard_pool(&_sapp.window_pool.pool);
  2409. }
  2410. /* returns pointer to window by id without matching id check */
  2411. _SOKOL_PRIVATE _sapp_window_t* _sapp_window_at(uint32_t win_id) {
  2412. SOKOL_ASSERT(SAPP_INVALID_ID != win_id);
  2413. int slot_index = _sapp_slot_index(win_id);
  2414. SOKOL_ASSERT((slot_index > _SAPP_INVALID_SLOT_INDEX) && (slot_index < _sapp.window_pool.pool.size));
  2415. SOKOL_ASSERT(_sapp.window_pool.windows);
  2416. return &_sapp.window_pool.windows[slot_index];
  2417. }
  2418. /* returns pointer to window with matching id check, may return 0 */
  2419. _SOKOL_PRIVATE _sapp_window_t* _sapp_lookup_window(uint32_t win_id) {
  2420. if (SAPP_INVALID_ID != win_id) {
  2421. _sapp_window_t* win = _sapp_window_at(win_id);
  2422. if (win->slot.id == win_id) {
  2423. return win;
  2424. }
  2425. }
  2426. return 0;
  2427. }
  2428. _SOKOL_PRIVATE sapp_window _sapp_make_window_id(uint32_t win_id) {
  2429. sapp_window res = { win_id };
  2430. return res;
  2431. }
  2432. _SOKOL_PRIVATE uint32_t _sapp_alloc_window_id(void) {
  2433. int slot_index = _sapp_pool_alloc_index(&_sapp.window_pool.pool);
  2434. if (_SAPP_INVALID_SLOT_INDEX != slot_index) {
  2435. return _sapp_slot_alloc(&_sapp.window_pool.pool, &_sapp.window_pool.windows[slot_index].slot, slot_index);
  2436. }
  2437. else {
  2438. /* pool is exhausted */
  2439. return SAPP_INVALID_ID;
  2440. }
  2441. }
  2442. _SOKOL_PRIVATE void _sapp_free_window_id(uint32_t win_id) {
  2443. SOKOL_ASSERT(SAPP_INVALID_ID != win_id);
  2444. _sapp_pool_free_index(&_sapp.window_pool.pool, _sapp_slot_index(win_id));
  2445. }
  2446. _SOKOL_PRIVATE bool _sapp_platform_create_window(_sapp_window_t* win) {
  2447. #if defined(_SAPP_MACOS)
  2448. return _sapp_macos_create_window(win);
  2449. #elif defined(_SAPP_IOS)
  2450. return _sapp_ios_create_window(win);
  2451. #elif defined(_SAPP_EMSCRIPTEN)
  2452. return _sapp_emsc_create_window(win);
  2453. #elif defined(_SAPP_WIN32)
  2454. return _sapp_win32_create_window(win);
  2455. #elif defined(_SAPP_UWP)
  2456. return _sapp_uwp_create_window(win);
  2457. #elif defined(_SAPP_ANDROID)
  2458. return _sapp_android_create_window(win);
  2459. #elif defined(_SAPP_LINUX)
  2460. return _sapp_x11_create_window(win);
  2461. #endif
  2462. }
  2463. _SOKOL_PRIVATE void _sapp_platform_destroy_window(_sapp_window_t* win) {
  2464. #if defined(_SAPP_MACOS)
  2465. _sapp_macos_destroy_window(win);
  2466. #elif defined(_SAPP_IOS)
  2467. _sapp_ios_destroy_window(win);
  2468. #elif defined(_SAPP_EMSCRIPTEN)
  2469. _sapp_emsc_destroy_window(win);
  2470. #elif defined(_SAPP_WIN32)
  2471. _sapp_win32_destroy_window(win);
  2472. #elif defined(_SAPP_UWP)
  2473. _sapp_uwp_destroy_window(win);
  2474. #elif defined(_SAPP_ANDROID)
  2475. _sapp_android_destroy_window(win);
  2476. #elif defined(_SAPP_LINUX)
  2477. _sapp_x11_destroy_window(win);
  2478. #endif
  2479. }
  2480. _SOKOL_PRIVATE void _sapp_platform_close_window(_sapp_window_t* win) {
  2481. #if defined(_SAPP_MACOS)
  2482. _sapp_macos_close_window(win);
  2483. #elif defined(_SAPP_IOS)
  2484. _sapp_ios_close_window(win);
  2485. #elif defined(_SAPP_EMSCRIPTEN)
  2486. _sapp_emsc_close_window(win);
  2487. #elif defined(_SAPP_WIN32)
  2488. _sapp_win32_close_window(win);
  2489. #elif defined(_SAPP_UWP)
  2490. _sapp_uwp_close_window(win);
  2491. #elif defined(_SAPP_ANDROID)
  2492. _sapp_android_close_window(win);
  2493. #elif defined(_SAPP_LINUX)
  2494. _sapp_x11_close_window(win);
  2495. #endif
  2496. }
  2497. _SOKOL_PRIVATE void _sapp_platform_show_window(_sapp_window_t* win) {
  2498. #if defined(_SAPP_MACOS)
  2499. _sapp_macos_show_window(win);
  2500. #elif defined(_SAPP_WIN32)
  2501. _sapp_win32_show_window(win);
  2502. #elif defined(_SAPP_LINUX)
  2503. _sapp_x11_close_window(win);
  2504. #else
  2505. _SOKOL_UNUSED(win);
  2506. #endif
  2507. }
  2508. _SOKOL_PRIVATE void _sapp_platform_hide_window(_sapp_window_t* win) {
  2509. #if defined(_SAPP_MACOS)
  2510. _sapp_macos_hide_window(win);
  2511. #elif defined(_SAPP_WIN32)
  2512. _sapp_win32_hide_window(win);
  2513. #elif defined(_SAPP_LINUX)
  2514. _sapp_x11_hide_window(win);
  2515. #else
  2516. _SOKOL_UNUSED(win);
  2517. #endif
  2518. }
  2519. _SOKOL_PRIVATE bool _sapp_platform_window_visible(_sapp_window_t* win) {
  2520. #if defined(_SAPP_MACOS)
  2521. return _sapp_macos_window_visible(win);
  2522. #elif defined(_SAPP_WIN32)
  2523. return _sapp_win32_window_visible(win);
  2524. #elif defined(_SAPP_LINUX)
  2525. return _sapp_x11_window_visible(win);
  2526. #else
  2527. _SOKOL_UNUSED(win);
  2528. return true;
  2529. #endif
  2530. }
  2531. _SOKOL_PRIVATE void _sapp_platform_focus_window(_sapp_window_t* win) {
  2532. #if defined(_SAPP_MACOS)
  2533. _sapp_macos_focus_window(win);
  2534. #elif defined(_SAPP_WIN32)
  2535. _sapp_win32_focus_window(win);
  2536. #elif defined(_SAPP_LINUX)
  2537. _sapp_x11_focus_window(win);
  2538. #else
  2539. _SOKOL_UNUSED(win);
  2540. #endif
  2541. }
  2542. _SOKOL_PRIVATE bool _sapp_platform_window_focused(_sapp_window_t* win) {
  2543. #if defined(_SAPP_MACOS)
  2544. return _sapp_macos_window_focused(win);
  2545. #elif defined(_SAPP_WIN32)
  2546. return _sapp_win32_window_focused(win);
  2547. #elif defined(_SAPP_LINUX)
  2548. return _sapp_x11_window_focused(win);
  2549. #else
  2550. _SOKOL_UNUSED(win);
  2551. return true;
  2552. #endif
  2553. }
  2554. _SOKOL_PRIVATE bool _sapp_platform_window_minimized(_sapp_window_t* win) {
  2555. #if defined(_SAPP_MACOS)
  2556. return _sapp_macos_window_minimized(win);
  2557. #elif defined(_SAPP_WIN32)
  2558. return _sapp_win32_window_minimized(win);
  2559. #elif defined(_SAPP_LINUX)
  2560. return _sapp_x11_window_minimized(win);
  2561. #else
  2562. _SOKOL_UNUSED(win);
  2563. return false;
  2564. #endif
  2565. }
  2566. _SOKOL_PRIVATE void _sapp_platform_set_window_pos(_sapp_window_t* win, int x, int y) {
  2567. #if defined(_SAPP_MACOS)
  2568. _sapp_macos_set_window_pos(win, x, y);
  2569. #elif defined(_SAPP_IOS)
  2570. _sapp_ios_set_window_pos(win, x, y);
  2571. #elif defined(_SAPP_EMSCRIPTEN)
  2572. _sapp_emsc_set_window_pos(win, x, y);
  2573. #elif defined(_SAPP_WIN32)
  2574. _sapp_win32_set_window_pos(win, x, y);
  2575. #elif defined(_SAPP_UWP)
  2576. _sapp_uwp_set_window_pos(win, x, y);
  2577. #elif defined(_SAPP_ANDROID)
  2578. _sapp_android_set_window_pos(win, x, y);
  2579. #elif defined(_SAPP_LINUX)
  2580. _sapp_x11_set_window_pos(win, x, y);
  2581. #endif
  2582. }
  2583. _SOKOL_PRIVATE void _sapp_platform_get_window_pos(_sapp_window_t* win, int* x, int* y) {
  2584. #if defined(_SAPP_MACOS)
  2585. _sapp_macos_get_window_pos(win, x, y);
  2586. #elif defined(_SAPP_IOS)
  2587. _sapp_ios_get_window_pos(win, x, y);
  2588. #elif defined(_SAPP_EMSCRIPTEN)
  2589. _sapp_emsc_get_window_pos(win, x, y);
  2590. #elif defined(_SAPP_WIN32)
  2591. _sapp_win32_get_window_pos(win, x, y);
  2592. #elif defined(_SAPP_UWP)
  2593. _sapp_uwp_get_window_pos(win, x, y);
  2594. #elif defined(_SAPP_ANDROID)
  2595. _sapp_android_get_window_pos(win, x, y);
  2596. #elif defined(_SAPP_LINUX)
  2597. _sapp_x11_get_window_pos(win, x, y);
  2598. #endif
  2599. }
  2600. _SOKOL_PRIVATE void _sapp_platform_set_window_size(_sapp_window_t* win, int w, int h) {
  2601. #if defined(_SAPP_MACOS)
  2602. _sapp_macos_set_window_size(win, w, h);
  2603. #elif defined(_SAPP_IOS)
  2604. _sapp_ios_set_window_size(win, w, h);
  2605. #elif defined(_SAPP_EMSCRIPTEN)
  2606. _sapp_emsc_set_window_size(win, w, h);
  2607. #elif defined(_SAPP_WIN32)
  2608. _sapp_win32_set_window_size(win, w, h);
  2609. #elif defined(_SAPP_UWP)
  2610. _sapp_uwp_set_window_size(win, w, h);
  2611. #elif defined(_SAPP_ANDROID)
  2612. _sapp_android_set_window_size(win, w, h);
  2613. #elif defined(_SAPP_LINUX)
  2614. _sapp_x11_set_window_size(win, w, h);
  2615. #endif
  2616. }
  2617. _SOKOL_PRIVATE void _sapp_platform_get_window_size(_sapp_window_t* win, int* w, int* h) {
  2618. #if defined(_SAPP_MACOS)
  2619. _sapp_macos_get_window_size(win, w, h);
  2620. #elif defined(_SAPP_IOS)
  2621. _sapp_ios_get_window_size(win, w, h);
  2622. #elif defined(_SAPP_EMSCRIPTEN)
  2623. _sapp_emsc_get_window_size(win, w, h);
  2624. #elif defined(_SAPP_WIN32)
  2625. _sapp_win32_get_window_size(win, w, h);
  2626. #elif defined(_SAPP_UWP)
  2627. _sapp_uwp_get_window_size(win, w, h);
  2628. #elif defined(_SAPP_ANDROID)
  2629. _sapp_android_get_window_size(win, w, h);
  2630. #elif defined(_SAPP_LINUX)
  2631. _sapp_x11_get_window_size(win, w, h);
  2632. #endif
  2633. }
  2634. _SOKOL_PRIVATE void _sapp_platform_activate_window_context(_sapp_window_t* win) {
  2635. #if defined(_SAPP_MACOS)
  2636. _sapp_macos_activate_window_context(win);
  2637. #elif defined(_SAPP_WIN32)
  2638. _sapp_win32_activate_window_context(win);
  2639. #elif defined(_SAPP_LINUX)
  2640. _sapp_x11_activate_window_context(win);
  2641. #else
  2642. _SOKOL_UNUSED(win);
  2643. #endif
  2644. }
  2645. _SOKOL_PRIVATE void _sapp_platform_set_icon(const sapp_icon_desc* desc, int num_images) {
  2646. #if defined(_SAPP_MACOS)
  2647. _sapp_macos_set_icon(desc, num_images);
  2648. #elif defined(_SAPP_WIN32)
  2649. _sapp_win32_set_icon(desc, num_images);
  2650. #elif defined(_SAPP_LINUX)
  2651. _sapp_x11_set_icon(desc, num_images);
  2652. #elif defined(_SAPP_EMSCRIPTEN)
  2653. _sapp_emsc_set_icon(desc, num_images);
  2654. #endif
  2655. }
  2656. _SOKOL_PRIVATE void _sapp_fail(const char* msg) {
  2657. if (_sapp.desc.fail_cb) {
  2658. _sapp.desc.fail_cb(msg);
  2659. }
  2660. else if (_sapp.desc.fail_userdata_cb) {
  2661. _sapp.desc.fail_userdata_cb(msg, _sapp.desc.fail_user_data);
  2662. }
  2663. else {
  2664. SOKOL_LOG(msg);
  2665. }
  2666. SOKOL_ABORT();
  2667. }
  2668. _SOKOL_PRIVATE void _sapp_call_init(void) {
  2669. SOKOL_ASSERT(!_sapp.init_called);
  2670. if (_sapp.desc.init_cb) {
  2671. _sapp.desc.init_cb();
  2672. }
  2673. else if (_sapp.desc.init_userdata_cb) {
  2674. _sapp.desc.init_userdata_cb(_sapp.desc.user_data);
  2675. }
  2676. _sapp.init_called = true;
  2677. }
  2678. _SOKOL_PRIVATE void _sapp_call_frame(void) {
  2679. if (_sapp.init_called && !_sapp.cleanup_called) {
  2680. if (_sapp.desc.frame_cb) {
  2681. _sapp.desc.frame_cb();
  2682. }
  2683. else if (_sapp.desc.frame_userdata_cb) {
  2684. _sapp.desc.frame_userdata_cb(_sapp.desc.user_data);
  2685. }
  2686. }
  2687. }
  2688. _SOKOL_PRIVATE void _sapp_call_cleanup(void) {
  2689. if (!_sapp.cleanup_called) {
  2690. if (_sapp.desc.cleanup_cb) {
  2691. _sapp.desc.cleanup_cb();
  2692. }
  2693. else if (_sapp.desc.cleanup_userdata_cb) {
  2694. _sapp.desc.cleanup_userdata_cb(_sapp.desc.user_data);
  2695. }
  2696. _sapp.cleanup_called = true;
  2697. }
  2698. }
  2699. _SOKOL_PRIVATE bool _sapp_call_event(const sapp_event* e) {
  2700. if (!_sapp.cleanup_called) {
  2701. if (_sapp.desc.event_cb) {
  2702. _sapp.desc.event_cb(e);
  2703. }
  2704. else if (_sapp.desc.event_userdata_cb) {
  2705. _sapp.desc.event_userdata_cb(e, _sapp.desc.user_data);
  2706. }
  2707. }
  2708. if (_sapp.event_consumed) {
  2709. _sapp.event_consumed = false;
  2710. return true;
  2711. }
  2712. else {
  2713. return false;
  2714. }
  2715. }
  2716. _SOKOL_PRIVATE void _sapp_init_event(_sapp_window_t* win, sapp_event_type type) {
  2717. memset(&_sapp.event, 0, sizeof(_sapp.event));
  2718. _sapp.event.type = type;
  2719. _sapp.event.window = _sapp_make_window_id(win->slot.id);
  2720. _sapp.event.frame_count = _sapp.frame_count;
  2721. _sapp.event.mouse_button = SAPP_MOUSEBUTTON_INVALID;
  2722. _sapp.event.window_width = win->window_width;
  2723. _sapp.event.window_height = win->window_height;
  2724. _sapp.event.framebuffer_width = win->framebuffer_width;
  2725. _sapp.event.framebuffer_height = win->framebuffer_height;
  2726. _sapp.event.mouse_x = win->mouse.x;
  2727. _sapp.event.mouse_y = win->mouse.y;
  2728. _sapp.event.mouse_dx = win->mouse.dx;
  2729. _sapp.event.mouse_dy = win->mouse.dy;
  2730. }
  2731. _SOKOL_PRIVATE bool _sapp_events_enabled(void) {
  2732. /* only send events when an event callback is set, and the init function was called */
  2733. return (_sapp.desc.event_cb || _sapp.desc.event_userdata_cb) && _sapp.init_called && !_sapp.cleanup_called;
  2734. }
  2735. _SOKOL_PRIVATE char* _sapp_dropped_file_path_ptr(int index) {
  2736. SOKOL_ASSERT(_sapp.drop.buffer);
  2737. SOKOL_ASSERT((index >= 0) && (index <= _sapp.drop.max_files));
  2738. int offset = index * _sapp.drop.max_path_length;
  2739. SOKOL_ASSERT(offset < _sapp.drop.buf_size);
  2740. return &_sapp.drop.buffer[offset];
  2741. }
  2742. /* Copy a string into a fixed size buffer with guaranteed zero-
  2743. termination.
  2744. Return false if the string didn't fit into the buffer and had to be clamped.
  2745. FIXME: Currently UTF-8 strings might become invalid if the string
  2746. is clamped, because the last zero-byte might be written into
  2747. the middle of a multi-byte sequence.
  2748. */
  2749. _SOKOL_PRIVATE bool _sapp_strcpy(const char* src, char* dst, int max_len) {
  2750. SOKOL_ASSERT(src && dst && (max_len > 0));
  2751. char* const end = &(dst[max_len-1]);
  2752. char c = 0;
  2753. for (int i = 0; i < max_len; i++) {
  2754. c = *src;
  2755. if (c != 0) {
  2756. src++;
  2757. }
  2758. *dst++ = c;
  2759. }
  2760. /* truncated? */
  2761. if (c != 0) {
  2762. *end = 0;
  2763. return false;
  2764. }
  2765. else {
  2766. return true;
  2767. }
  2768. }
  2769. _SOKOL_PRIVATE sapp_desc _sapp_desc_defaults(const sapp_desc* in_desc) {
  2770. sapp_desc desc = *in_desc;
  2771. desc.window_pool_size = _sapp_def(desc.window_pool_size, _SAPP_DEFAULT_POOL_SIZE);
  2772. desc.window_title = _sapp_def(desc.window_title, "sokol-app");
  2773. desc.width = _sapp_def(desc.width, 640);
  2774. desc.height = _sapp_def(desc.height, 480);
  2775. desc.sample_count = _sapp_def(desc.sample_count, 1);
  2776. desc.swap_interval = _sapp_def(desc.swap_interval, 1);
  2777. desc.html5.canvas_name = _sapp_def(desc.html5.canvas_name, "canvas");
  2778. desc.max_dropped_files = _sapp_def(desc.max_dropped_files, 1);
  2779. desc.max_dropped_file_path_length = _sapp_def(desc.max_dropped_file_path_length, 2048);
  2780. desc.clipboard_size = _sapp_def(desc.clipboard_size, 8192);
  2781. return desc;
  2782. }
  2783. _SOKOL_PRIVATE sapp_window_desc _sapp_window_desc_defaults(const sapp_window_desc* in_desc) {
  2784. sapp_window_desc desc = *in_desc;
  2785. desc.title = _sapp_def(desc.title, "sokol-app");
  2786. desc.width = _sapp_def(desc.width, 640);
  2787. desc.height = _sapp_def(desc.height, 480);
  2788. desc.sample_count = _sapp_def(desc.sample_count, 1);
  2789. desc.swap_interval = _sapp_def(desc.swap_interval, 1);
  2790. return desc;
  2791. }
  2792. _SOKOL_PRIVATE sapp_window_desc _sapp_desc_to_window_desc(const sapp_desc* desc) {
  2793. sapp_window_desc wdesc;
  2794. memset(&wdesc, 0, sizeof(wdesc));
  2795. wdesc.title = desc->window_title;
  2796. wdesc.x = desc->x;
  2797. wdesc.y = desc->y;
  2798. wdesc.width = desc->width;
  2799. wdesc.height = desc->height;
  2800. wdesc.sample_count = desc->sample_count;
  2801. wdesc.swap_interval = desc->swap_interval;
  2802. wdesc.high_dpi = desc->high_dpi;
  2803. wdesc.fullscreen = desc->fullscreen;
  2804. wdesc.alpha = desc->alpha;
  2805. wdesc.user_cursor = desc->user_cursor;
  2806. wdesc.no_decoration = desc->no_decoration;
  2807. wdesc.user_data = desc->user_data;
  2808. return wdesc;
  2809. }
  2810. _SOKOL_PRIVATE void _sapp_platform_init_state(void) {
  2811. #if defined(_SAPP_MACOS)
  2812. _sapp_macos_init_state();
  2813. #elif defined(_SAPP_IOS)
  2814. _sapp_ios_init_state();
  2815. #elif defined(_SAPP_EMSCRIPTEN)
  2816. _sapp_emsc_init_state();
  2817. #elif defined(_SAPP_WIN32)
  2818. _sapp_win32_init_state();
  2819. #elif defined(_SAPP_UWP)
  2820. _sapp_uwp_init_state();
  2821. #elif defined(_SAPP_ANDROID)
  2822. _sapp_android_init_state();
  2823. #elif defined(_SAPP_LINUX)
  2824. _sapp_x11_init_state();
  2825. #endif
  2826. }
  2827. _SOKOL_PRIVATE void _sapp_platform_discard_state(void) {
  2828. #if defined(_SAPP_MACOS)
  2829. _sapp_macos_discard_state();
  2830. #elif defined(_SAPP_IOS)
  2831. _sapp_ios_discard_state();
  2832. #elif defined(_SAPP_EMSCRIPTEN)
  2833. _sapp_emsc_discard_state();
  2834. #elif defined(_SAPP_WIN32)
  2835. _sapp_win32_discard_state();
  2836. #elif defined(_SAPP_UWP)
  2837. _sapp_uwp_discard_state();
  2838. #elif defined(_SAPP_ANDROID)
  2839. _sapp_android_discard_state();
  2840. #elif defined(_SAPP_LINUX)
  2841. _sapp_x11_discard_state();
  2842. #endif
  2843. }
  2844. _SOKOL_PRIVATE void _sapp_init_clipboard(_sapp_clipboard_t* clipboard, const sapp_desc* desc) {
  2845. SOKOL_ASSERT(clipboard && desc);
  2846. SOKOL_ASSERT(0 == clipboard->buffer);
  2847. clipboard->enabled = desc->enable_clipboard;
  2848. if (clipboard->enabled) {
  2849. clipboard->buf_size = desc->clipboard_size;
  2850. clipboard->buffer = (char*) SOKOL_CALLOC(1, (size_t)clipboard->buf_size);
  2851. if (0 == clipboard->buffer) {
  2852. SOKOL_LOG("failed to allocate clipboard buffer!");
  2853. clipboard->enabled = false;
  2854. }
  2855. }
  2856. }
  2857. _SOKOL_PRIVATE void _sapp_discard_clipboard(_sapp_clipboard_t* clipboard) {
  2858. SOKOL_ASSERT(clipboard);
  2859. if (0 != clipboard->buffer) {
  2860. SOKOL_FREE(clipboard->buffer);
  2861. }
  2862. _SAPP_CLEAR_PTR(_sapp_clipboard_t, clipboard);
  2863. }
  2864. _SOKOL_PRIVATE void _sapp_init_drop(_sapp_drop_t* drop, const sapp_desc* desc) {
  2865. SOKOL_ASSERT(drop && desc);
  2866. SOKOL_ASSERT(0 == drop->buffer);
  2867. drop->enabled = desc->enable_dragndrop;
  2868. if (drop->enabled) {
  2869. drop->max_files = desc->max_dropped_files;
  2870. drop->max_path_length = desc->max_dropped_file_path_length;
  2871. drop->buf_size = drop->max_files * drop->max_path_length;
  2872. drop->buffer = (char*) SOKOL_CALLOC(1, (size_t)drop->buf_size);
  2873. if (0 == drop->buffer) {
  2874. SOKOL_LOG("failed to allocate drag'n'drop buffer!");
  2875. drop->enabled = false;
  2876. }
  2877. }
  2878. }
  2879. _SOKOL_PRIVATE void _sapp_discard_drop(_sapp_drop_t* drop) {
  2880. SOKOL_ASSERT(drop);
  2881. if (0 != drop->buffer) {
  2882. SOKOL_FREE(drop->buffer);
  2883. }
  2884. _SAPP_CLEAR_PTR(_sapp_drop_t, drop);
  2885. }
  2886. _SOKOL_PRIVATE void _sapp_clear_drop_buffer(_sapp_drop_t* drop) {
  2887. if (drop->enabled) {
  2888. SOKOL_ASSERT(drop->buffer);
  2889. memset(drop->buffer, 0, (size_t)drop->buf_size);
  2890. }
  2891. }
  2892. _SOKOL_PRIVATE void _sapp_destroy_window(uint32_t win_id) {
  2893. _sapp_window_t* win = _sapp_lookup_window(win_id);
  2894. SOKOL_ASSERT(win);
  2895. if (_sapp_events_enabled() && (win_id != _sapp.main_window_id)) {
  2896. _sapp_init_event(win, SAPP_EVENTTYPE_WINDOW_CLOSED);
  2897. _sapp_call_event(&_sapp.event);
  2898. }
  2899. _sapp_platform_destroy_window(win);
  2900. _sapp_free_window_id(win_id);
  2901. _SAPP_CLEAR_PTR(_sapp_window_t, win);
  2902. }
  2903. _SOKOL_PRIVATE uint32_t _sapp_create_window(const sapp_window_desc* desc) {
  2904. SOKOL_ASSERT(desc);
  2905. const uint32_t win_id = _sapp_alloc_window_id();
  2906. if (SAPP_INVALID_ID == win_id) {
  2907. SOKOL_LOG("window pool exhausted")
  2908. return SAPP_INVALID_ID;
  2909. }
  2910. // platform-agnostic initialization
  2911. _sapp_window_t* win = _sapp_window_at(win_id);
  2912. SOKOL_ASSERT(win);
  2913. win->desc = *desc;
  2914. win->pos_x = desc->x;
  2915. win->pos_y = desc->y;
  2916. win->window_width = desc->width;
  2917. win->window_height = desc->height;
  2918. win->framebuffer_width = desc->width;
  2919. win->framebuffer_height = desc->height;
  2920. win->dpi_scale = 1.0f;
  2921. win->fullscreen = desc->fullscreen;
  2922. win->mouse.shown = true;
  2923. _sapp_strcpy(desc->title, &win->title[0], sizeof(win->title));
  2924. win->desc.title = &win->title[0]; // redirect transient pointer to persistent copy
  2925. // platform-specific window creation and initialization
  2926. if (!_sapp_platform_create_window(win)) {
  2927. return SAPP_INVALID_ID;
  2928. }
  2929. return win_id;
  2930. }
  2931. _SOKOL_PRIVATE void _sapp_destroy_all_windows(void) {
  2932. SOKOL_ASSERT(_sapp.window_pool.windows);
  2933. // make sure to destroy the main window last
  2934. for (int i = 0; i < _sapp.window_pool.pool.size; i++) {
  2935. uint32_t win_id = _sapp.window_pool.windows[i].slot.id;
  2936. if ((SAPP_INVALID_ID != win_id) && (_sapp.main_window_id != win_id)) {
  2937. _sapp_destroy_window(win_id);
  2938. }
  2939. }
  2940. _sapp_destroy_window(_sapp.main_window_id);
  2941. }
  2942. _SOKOL_PRIVATE void _sapp_init_state(const sapp_desc* desc) {
  2943. _SAPP_CLEAR_ITEM(_sapp_t, _sapp);
  2944. _sapp.desc = _sapp_desc_defaults(desc);
  2945. _sapp.first_frame = true;
  2946. _sapp_setup_window_pool(&_sapp.desc);
  2947. _sapp_init_clipboard(&_sapp.clipboard, desc);
  2948. _sapp_init_drop(&_sapp.drop, desc);
  2949. _sapp_platform_init_state();
  2950. // copy title string in desc to backing store and patch transient pointer
  2951. _sapp_strcpy(desc->window_title, &_sapp.desc_window_title[0], sizeof(_sapp.desc_window_title));
  2952. _sapp.desc.window_title = &_sapp.desc_window_title[0];
  2953. // FIXME: what about the icon image pointers?
  2954. }
  2955. _SOKOL_PRIVATE void _sapp_discard_state(void) {
  2956. _sapp_call_cleanup();
  2957. _sapp_destroy_all_windows();
  2958. _sapp_discard_window_pool();
  2959. _sapp_platform_discard_state();
  2960. _sapp_discard_drop(&_sapp.drop);
  2961. _sapp_discard_clipboard(&_sapp.clipboard);
  2962. if (_sapp.default_icon_pixels) {
  2963. SOKOL_FREE((void*)_sapp.default_icon_pixels);
  2964. _sapp.default_icon_pixels = 0;
  2965. }
  2966. _SAPP_CLEAR_ITEM(_sapp_t, _sapp);
  2967. }
  2968. _SOKOL_PRIVATE sapp_keycode _sapp_translate_key(int scan_code) {
  2969. if ((scan_code >= 0) && (scan_code < SAPP_MAX_KEYCODES)) {
  2970. return _sapp.keycodes[scan_code];
  2971. }
  2972. else {
  2973. return SAPP_KEYCODE_INVALID;
  2974. }
  2975. }
  2976. _SOKOL_PRIVATE void _sapp_frame(void) {
  2977. if (_sapp.first_frame) {
  2978. _sapp.first_frame = false;
  2979. _sapp_call_init();
  2980. }
  2981. _sapp_call_frame();
  2982. _sapp.frame_count++;
  2983. }
  2984. _SOKOL_PRIVATE bool _sapp_image_validate(const sapp_image_desc* desc) {
  2985. SOKOL_ASSERT(desc->width > 0);
  2986. SOKOL_ASSERT(desc->height > 0);
  2987. SOKOL_ASSERT(desc->pixels.ptr != 0);
  2988. SOKOL_ASSERT(desc->pixels.size > 0);
  2989. const size_t wh_size = (size_t)(desc->width * desc->height) * sizeof(uint32_t);
  2990. if (wh_size != desc->pixels.size) {
  2991. SOKOL_LOG("Image data size mismatch (must be width*height*4 bytes)\n");
  2992. return false;
  2993. }
  2994. return true;
  2995. }
  2996. _SOKOL_PRIVATE int _sapp_image_bestmatch(const sapp_image_desc image_descs[], int num_images, int width, int height) {
  2997. int least_diff = 0x7FFFFFFF;
  2998. int least_index = 0;
  2999. for (int i = 0; i < num_images; i++) {
  3000. int diff = (image_descs[i].width * image_descs[i].height) - (width * height);
  3001. if (diff < 0) {
  3002. diff = -diff;
  3003. }
  3004. if (diff < least_diff) {
  3005. least_diff = diff;
  3006. least_index = i;
  3007. }
  3008. }
  3009. return least_index;
  3010. }
  3011. _SOKOL_PRIVATE int _sapp_icon_num_images(const sapp_icon_desc* desc) {
  3012. int index = 0;
  3013. for (; index < SAPP_MAX_ICONIMAGES; index++) {
  3014. if (0 == desc->images[index].pixels.ptr) {
  3015. break;
  3016. }
  3017. }
  3018. return index;
  3019. }
  3020. _SOKOL_PRIVATE bool _sapp_validate_icon_desc(const sapp_icon_desc* desc, int num_images) {
  3021. SOKOL_ASSERT(num_images <= SAPP_MAX_ICONIMAGES);
  3022. for (int i = 0; i < num_images; i++) {
  3023. const sapp_image_desc* img_desc = &desc->images[i];
  3024. if (!_sapp_image_validate(img_desc)) {
  3025. return false;
  3026. }
  3027. }
  3028. return true;
  3029. }
  3030. _SOKOL_PRIVATE void _sapp_setup_default_icon(void) {
  3031. SOKOL_ASSERT(0 == _sapp.default_icon_pixels);
  3032. const int num_icons = 3;
  3033. const int icon_sizes[3] = { 16, 32, 64 }; // must be multiple of 8!
  3034. // allocate a pixel buffer for all icon pixels
  3035. int all_num_pixels = 0;
  3036. for (int i = 0; i < num_icons; i++) {
  3037. all_num_pixels += icon_sizes[i] * icon_sizes[i];
  3038. }
  3039. _sapp.default_icon_pixels = (uint32_t*) SOKOL_CALLOC((size_t)all_num_pixels, sizeof(uint32_t));
  3040. // initialize default_icon_desc struct
  3041. uint32_t* dst = _sapp.default_icon_pixels;
  3042. const uint32_t* dst_end = dst + all_num_pixels;
  3043. _SOKOL_UNUSED(dst_end); // silence unused warning in release mode
  3044. for (int i = 0; i < num_icons; i++) {
  3045. const int dim = (int) icon_sizes[i];
  3046. const int num_pixels = dim * dim;
  3047. sapp_image_desc* img_desc = &_sapp.default_icon_desc.images[i];
  3048. img_desc->width = dim;
  3049. img_desc->height = dim;
  3050. img_desc->pixels.ptr = dst;
  3051. img_desc->pixels.size = (size_t)num_pixels * sizeof(uint32_t);
  3052. dst += num_pixels;
  3053. }
  3054. SOKOL_ASSERT(dst == dst_end);
  3055. // Amstrad CPC font 'S'
  3056. const uint8_t tile[8] = {
  3057. 0x3C,
  3058. 0x66,
  3059. 0x60,
  3060. 0x3C,
  3061. 0x06,
  3062. 0x66,
  3063. 0x3C,
  3064. 0x00,
  3065. };
  3066. // rainbow colors
  3067. const uint32_t colors[8] = {
  3068. 0xFF4370FF,
  3069. 0xFF26A7FF,
  3070. 0xFF58EEFF,
  3071. 0xFF57E1D4,
  3072. 0xFF65CC9C,
  3073. 0xFF6ABB66,
  3074. 0xFFF5A542,
  3075. 0xFFC2577E,
  3076. };
  3077. dst = _sapp.default_icon_pixels;
  3078. const uint32_t blank = 0x00FFFFFF;
  3079. const uint32_t shadow = 0xFF000000;
  3080. for (int i = 0; i < num_icons; i++) {
  3081. const int dim = icon_sizes[i];
  3082. SOKOL_ASSERT((dim % 8) == 0);
  3083. const int scale = dim / 8;
  3084. for (int ty = 0, y = 0; ty < 8; ty++) {
  3085. const uint32_t color = colors[ty];
  3086. for (int sy = 0; sy < scale; sy++, y++) {
  3087. uint8_t bits = tile[ty];
  3088. for (int tx = 0, x = 0; tx < 8; tx++, bits<<=1) {
  3089. uint32_t pixel = (0 == (bits & 0x80)) ? blank : color;
  3090. for (int sx = 0; sx < scale; sx++, x++) {
  3091. SOKOL_ASSERT(dst < dst_end);
  3092. *dst++ = pixel;
  3093. }
  3094. }
  3095. }
  3096. }
  3097. }
  3098. SOKOL_ASSERT(dst == dst_end);
  3099. // right shadow
  3100. dst = _sapp.default_icon_pixels;
  3101. for (int i = 0; i < num_icons; i++) {
  3102. const int dim = icon_sizes[i];
  3103. for (int y = 0; y < dim; y++) {
  3104. uint32_t prev_color = blank;
  3105. for (int x = 0; x < dim; x++) {
  3106. const int dst_index = y * dim + x;
  3107. const uint32_t cur_color = dst[dst_index];
  3108. if ((cur_color == blank) && (prev_color != blank)) {
  3109. dst[dst_index] = shadow;
  3110. }
  3111. prev_color = cur_color;
  3112. }
  3113. }
  3114. dst += dim * dim;
  3115. }
  3116. SOKOL_ASSERT(dst == dst_end);
  3117. // bottom shadow
  3118. dst = _sapp.default_icon_pixels;
  3119. for (int i = 0; i < num_icons; i++) {
  3120. const int dim = icon_sizes[i];
  3121. for (int x = 0; x < dim; x++) {
  3122. uint32_t prev_color = blank;
  3123. for (int y = 0; y < dim; y++) {
  3124. const int dst_index = y * dim + x;
  3125. const uint32_t cur_color = dst[dst_index];
  3126. if ((cur_color == blank) && (prev_color != blank)) {
  3127. dst[dst_index] = shadow;
  3128. }
  3129. prev_color = cur_color;
  3130. }
  3131. }
  3132. dst += dim * dim;
  3133. }
  3134. SOKOL_ASSERT(dst == dst_end);
  3135. }
  3136. _SOKOL_PRIVATE void _sapp_set_icon(const sapp_icon_desc* desc) {
  3137. if (desc->sokol_default) {
  3138. if (0 == _sapp.default_icon_pixels) {
  3139. _sapp_setup_default_icon();
  3140. }
  3141. SOKOL_ASSERT(0 != _sapp.default_icon_pixels);
  3142. desc = &_sapp.default_icon_desc;
  3143. }
  3144. const int num_images = _sapp_icon_num_images(desc);
  3145. if (num_images == 0) {
  3146. return;
  3147. }
  3148. SOKOL_ASSERT((num_images > 0) && (num_images <= SAPP_MAX_ICONIMAGES));
  3149. if (!_sapp_validate_icon_desc(desc, num_images)) {
  3150. return;
  3151. }
  3152. _sapp_platform_set_icon(desc, num_images);
  3153. }
  3154. /*== MacOS/iOS ===============================================================*/
  3155. #if defined(_SAPP_APPLE)
  3156. #if __has_feature(objc_arc)
  3157. #define _SAPP_OBJC_RELEASE(obj) { obj = nil; }
  3158. #else
  3159. #define _SAPP_OBJC_RELEASE(obj) { [obj release]; obj = nil; }
  3160. #endif
  3161. /*== MacOS ===================================================================*/
  3162. #if defined(_SAPP_MACOS)
  3163. _SOKOL_PRIVATE void _sapp_macos_init_keytable(void) {
  3164. _sapp.keycodes[0x1D] = SAPP_KEYCODE_0;
  3165. _sapp.keycodes[0x12] = SAPP_KEYCODE_1;
  3166. _sapp.keycodes[0x13] = SAPP_KEYCODE_2;
  3167. _sapp.keycodes[0x14] = SAPP_KEYCODE_3;
  3168. _sapp.keycodes[0x15] = SAPP_KEYCODE_4;
  3169. _sapp.keycodes[0x17] = SAPP_KEYCODE_5;
  3170. _sapp.keycodes[0x16] = SAPP_KEYCODE_6;
  3171. _sapp.keycodes[0x1A] = SAPP_KEYCODE_7;
  3172. _sapp.keycodes[0x1C] = SAPP_KEYCODE_8;
  3173. _sapp.keycodes[0x19] = SAPP_KEYCODE_9;
  3174. _sapp.keycodes[0x00] = SAPP_KEYCODE_A;
  3175. _sapp.keycodes[0x0B] = SAPP_KEYCODE_B;
  3176. _sapp.keycodes[0x08] = SAPP_KEYCODE_C;
  3177. _sapp.keycodes[0x02] = SAPP_KEYCODE_D;
  3178. _sapp.keycodes[0x0E] = SAPP_KEYCODE_E;
  3179. _sapp.keycodes[0x03] = SAPP_KEYCODE_F;
  3180. _sapp.keycodes[0x05] = SAPP_KEYCODE_G;
  3181. _sapp.keycodes[0x04] = SAPP_KEYCODE_H;
  3182. _sapp.keycodes[0x22] = SAPP_KEYCODE_I;
  3183. _sapp.keycodes[0x26] = SAPP_KEYCODE_J;
  3184. _sapp.keycodes[0x28] = SAPP_KEYCODE_K;
  3185. _sapp.keycodes[0x25] = SAPP_KEYCODE_L;
  3186. _sapp.keycodes[0x2E] = SAPP_KEYCODE_M;
  3187. _sapp.keycodes[0x2D] = SAPP_KEYCODE_N;
  3188. _sapp.keycodes[0x1F] = SAPP_KEYCODE_O;
  3189. _sapp.keycodes[0x23] = SAPP_KEYCODE_P;
  3190. _sapp.keycodes[0x0C] = SAPP_KEYCODE_Q;
  3191. _sapp.keycodes[0x0F] = SAPP_KEYCODE_R;
  3192. _sapp.keycodes[0x01] = SAPP_KEYCODE_S;
  3193. _sapp.keycodes[0x11] = SAPP_KEYCODE_T;
  3194. _sapp.keycodes[0x20] = SAPP_KEYCODE_U;
  3195. _sapp.keycodes[0x09] = SAPP_KEYCODE_V;
  3196. _sapp.keycodes[0x0D] = SAPP_KEYCODE_W;
  3197. _sapp.keycodes[0x07] = SAPP_KEYCODE_X;
  3198. _sapp.keycodes[0x10] = SAPP_KEYCODE_Y;
  3199. _sapp.keycodes[0x06] = SAPP_KEYCODE_Z;
  3200. _sapp.keycodes[0x27] = SAPP_KEYCODE_APOSTROPHE;
  3201. _sapp.keycodes[0x2A] = SAPP_KEYCODE_BACKSLASH;
  3202. _sapp.keycodes[0x2B] = SAPP_KEYCODE_COMMA;
  3203. _sapp.keycodes[0x18] = SAPP_KEYCODE_EQUAL;
  3204. _sapp.keycodes[0x32] = SAPP_KEYCODE_GRAVE_ACCENT;
  3205. _sapp.keycodes[0x21] = SAPP_KEYCODE_LEFT_BRACKET;
  3206. _sapp.keycodes[0x1B] = SAPP_KEYCODE_MINUS;
  3207. _sapp.keycodes[0x2F] = SAPP_KEYCODE_PERIOD;
  3208. _sapp.keycodes[0x1E] = SAPP_KEYCODE_RIGHT_BRACKET;
  3209. _sapp.keycodes[0x29] = SAPP_KEYCODE_SEMICOLON;
  3210. _sapp.keycodes[0x2C] = SAPP_KEYCODE_SLASH;
  3211. _sapp.keycodes[0x0A] = SAPP_KEYCODE_WORLD_1;
  3212. _sapp.keycodes[0x33] = SAPP_KEYCODE_BACKSPACE;
  3213. _sapp.keycodes[0x39] = SAPP_KEYCODE_CAPS_LOCK;
  3214. _sapp.keycodes[0x75] = SAPP_KEYCODE_DELETE;
  3215. _sapp.keycodes[0x7D] = SAPP_KEYCODE_DOWN;
  3216. _sapp.keycodes[0x77] = SAPP_KEYCODE_END;
  3217. _sapp.keycodes[0x24] = SAPP_KEYCODE_ENTER;
  3218. _sapp.keycodes[0x35] = SAPP_KEYCODE_ESCAPE;
  3219. _sapp.keycodes[0x7A] = SAPP_KEYCODE_F1;
  3220. _sapp.keycodes[0x78] = SAPP_KEYCODE_F2;
  3221. _sapp.keycodes[0x63] = SAPP_KEYCODE_F3;
  3222. _sapp.keycodes[0x76] = SAPP_KEYCODE_F4;
  3223. _sapp.keycodes[0x60] = SAPP_KEYCODE_F5;
  3224. _sapp.keycodes[0x61] = SAPP_KEYCODE_F6;
  3225. _sapp.keycodes[0x62] = SAPP_KEYCODE_F7;
  3226. _sapp.keycodes[0x64] = SAPP_KEYCODE_F8;
  3227. _sapp.keycodes[0x65] = SAPP_KEYCODE_F9;
  3228. _sapp.keycodes[0x6D] = SAPP_KEYCODE_F10;
  3229. _sapp.keycodes[0x67] = SAPP_KEYCODE_F11;
  3230. _sapp.keycodes[0x6F] = SAPP_KEYCODE_F12;
  3231. _sapp.keycodes[0x69] = SAPP_KEYCODE_F13;
  3232. _sapp.keycodes[0x6B] = SAPP_KEYCODE_F14;
  3233. _sapp.keycodes[0x71] = SAPP_KEYCODE_F15;
  3234. _sapp.keycodes[0x6A] = SAPP_KEYCODE_F16;
  3235. _sapp.keycodes[0x40] = SAPP_KEYCODE_F17;
  3236. _sapp.keycodes[0x4F] = SAPP_KEYCODE_F18;
  3237. _sapp.keycodes[0x50] = SAPP_KEYCODE_F19;
  3238. _sapp.keycodes[0x5A] = SAPP_KEYCODE_F20;
  3239. _sapp.keycodes[0x73] = SAPP_KEYCODE_HOME;
  3240. _sapp.keycodes[0x72] = SAPP_KEYCODE_INSERT;
  3241. _sapp.keycodes[0x7B] = SAPP_KEYCODE_LEFT;
  3242. _sapp.keycodes[0x3A] = SAPP_KEYCODE_LEFT_ALT;
  3243. _sapp.keycodes[0x3B] = SAPP_KEYCODE_LEFT_CONTROL;
  3244. _sapp.keycodes[0x38] = SAPP_KEYCODE_LEFT_SHIFT;
  3245. _sapp.keycodes[0x37] = SAPP_KEYCODE_LEFT_SUPER;
  3246. _sapp.keycodes[0x6E] = SAPP_KEYCODE_MENU;
  3247. _sapp.keycodes[0x47] = SAPP_KEYCODE_NUM_LOCK;
  3248. _sapp.keycodes[0x79] = SAPP_KEYCODE_PAGE_DOWN;
  3249. _sapp.keycodes[0x74] = SAPP_KEYCODE_PAGE_UP;
  3250. _sapp.keycodes[0x7C] = SAPP_KEYCODE_RIGHT;
  3251. _sapp.keycodes[0x3D] = SAPP_KEYCODE_RIGHT_ALT;
  3252. _sapp.keycodes[0x3E] = SAPP_KEYCODE_RIGHT_CONTROL;
  3253. _sapp.keycodes[0x3C] = SAPP_KEYCODE_RIGHT_SHIFT;
  3254. _sapp.keycodes[0x36] = SAPP_KEYCODE_RIGHT_SUPER;
  3255. _sapp.keycodes[0x31] = SAPP_KEYCODE_SPACE;
  3256. _sapp.keycodes[0x30] = SAPP_KEYCODE_TAB;
  3257. _sapp.keycodes[0x7E] = SAPP_KEYCODE_UP;
  3258. _sapp.keycodes[0x52] = SAPP_KEYCODE_KP_0;
  3259. _sapp.keycodes[0x53] = SAPP_KEYCODE_KP_1;
  3260. _sapp.keycodes[0x54] = SAPP_KEYCODE_KP_2;
  3261. _sapp.keycodes[0x55] = SAPP_KEYCODE_KP_3;
  3262. _sapp.keycodes[0x56] = SAPP_KEYCODE_KP_4;
  3263. _sapp.keycodes[0x57] = SAPP_KEYCODE_KP_5;
  3264. _sapp.keycodes[0x58] = SAPP_KEYCODE_KP_6;
  3265. _sapp.keycodes[0x59] = SAPP_KEYCODE_KP_7;
  3266. _sapp.keycodes[0x5B] = SAPP_KEYCODE_KP_8;
  3267. _sapp.keycodes[0x5C] = SAPP_KEYCODE_KP_9;
  3268. _sapp.keycodes[0x45] = SAPP_KEYCODE_KP_ADD;
  3269. _sapp.keycodes[0x41] = SAPP_KEYCODE_KP_DECIMAL;
  3270. _sapp.keycodes[0x4B] = SAPP_KEYCODE_KP_DIVIDE;
  3271. _sapp.keycodes[0x4C] = SAPP_KEYCODE_KP_ENTER;
  3272. _sapp.keycodes[0x51] = SAPP_KEYCODE_KP_EQUAL;
  3273. _sapp.keycodes[0x43] = SAPP_KEYCODE_KP_MULTIPLY;
  3274. _sapp.keycodes[0x4E] = SAPP_KEYCODE_KP_SUBTRACT;
  3275. }
  3276. /* called from _sapp_init_state() */
  3277. _SOKOL_PRIVATE void _sapp_macos_init_state(void) {
  3278. _sapp_macos_init_keytable();
  3279. // set the application dock icon as early as possible, otherwise
  3280. // the dummy icon will be visible for a short time
  3281. _sapp_set_icon(&_sapp.desc.icon);
  3282. NSApp.activationPolicy = NSApplicationActivationPolicyRegular;
  3283. _sapp.macos.app_delegate = [[_sapp_macos_app_delegate alloc] init];
  3284. NSApp.delegate = _sapp.macos.app_delegate;
  3285. [NSApp activateIgnoringOtherApps:YES];
  3286. #if defined(SOKOL_METAL)
  3287. _sapp.macos.mtl_device = MTLCreateSystemDefaultDevice();
  3288. #endif
  3289. }
  3290. /* called from _sapp_discard_state() */
  3291. _SOKOL_PRIVATE void _sapp_macos_discard_state(void) {
  3292. CVDisplayLinkStop(_sapp.macos.display_link);
  3293. CVDisplayLinkRelease(_sapp.macos.display_link);
  3294. _sapp.macos.display_link = nil;
  3295. // NOTE: it's safe to call [release] on a nil object
  3296. _SAPP_OBJC_RELEASE(_sapp.macos.app_delegate);
  3297. #if defined(SOKOL_METAL)
  3298. _SAPP_OBJC_RELEASE(_sapp.macos.mtl_device);
  3299. #endif
  3300. }
  3301. _SOKOL_PRIVATE void _sapp_macos_run(const sapp_desc* desc) {
  3302. [NSApplication sharedApplication];
  3303. _sapp_init_state(desc);
  3304. [NSApp run];
  3305. }
  3306. _SOKOL_PRIVATE void _sapp_macos_app_event(_sapp_window_t* win, sapp_event_type type) {
  3307. if (_sapp_events_enabled()) {
  3308. _sapp_init_event(win, type);
  3309. _sapp_call_event(&_sapp.event);
  3310. }
  3311. }
  3312. /* NOTE: unlike the iOS version of this function, the macOS version
  3313. can dynamically update the DPI scaling factor when a window is moved
  3314. between HighDPI / LowDPI screens.
  3315. */
  3316. _SOKOL_PRIVATE void _sapp_macos_update_dimensions(_sapp_window_t* win) {
  3317. CGFloat backing_scale_factor = [win->macos.window screen].backingScaleFactor;
  3318. if (win->desc.high_dpi) {
  3319. win->dpi_scale = backing_scale_factor;
  3320. }
  3321. else {
  3322. win->dpi_scale = 1.0f;
  3323. }
  3324. const NSRect bounds = [win->macos.view bounds];
  3325. win->window_width = bounds.size.width;
  3326. win->window_height = bounds.size.height;
  3327. #if defined(SOKOL_METAL)
  3328. win->framebuffer_width = bounds.size.width * win->dpi_scale;
  3329. win->framebuffer_height = bounds.size.height * win->dpi_scale;
  3330. const CGSize fb_size = win->macos.view.drawableSize;
  3331. const int cur_fb_width = (int) fb_size.width;
  3332. const int cur_fb_height = (int) fb_size.height;
  3333. const bool dim_changed = (win->framebuffer_width != cur_fb_width) ||
  3334. (win->framebuffer_height != cur_fb_height);
  3335. #elif defined(SOKOL_GLCORE33)
  3336. const int cur_fb_width = (int) bounds.size.width * win->dpi_scale;
  3337. const int cur_fb_height = (int) bounds.size.height * win->dpi_scale;
  3338. const bool dim_changed = (win->framebuffer_width != cur_fb_width) ||
  3339. (win->framebuffer_height != cur_fb_height);
  3340. win->framebuffer_width = cur_fb_width;
  3341. win->framebuffer_height = cur_fb_height;
  3342. #endif
  3343. if (win->framebuffer_width == 0) {
  3344. win->framebuffer_width = 1;
  3345. }
  3346. if (win->framebuffer_height == 0) {
  3347. win->framebuffer_height = 1;
  3348. }
  3349. if (win->window_width == 0) {
  3350. win->window_width = 1;
  3351. }
  3352. if (win->window_height == 0) {
  3353. win->window_height = 1;
  3354. }
  3355. if (dim_changed) {
  3356. #if defined(SOKOL_METAL)
  3357. CGSize drawable_size = { (CGFloat) win->framebuffer_width, (CGFloat) win->framebuffer_height };
  3358. win->macos.view.drawableSize = drawable_size;
  3359. #else
  3360. // FIXME: nothing to do here?
  3361. #endif
  3362. if (!_sapp.first_frame) {
  3363. _sapp_macos_app_event(win, SAPP_EVENTTYPE_RESIZED);
  3364. }
  3365. }
  3366. }
  3367. _SOKOL_PRIVATE bool _sapp_macos_create_window(_sapp_window_t* win) {
  3368. SOKOL_ASSERT(win);
  3369. if (win->fullscreen) {
  3370. NSRect screen_rect = NSScreen.mainScreen.frame;
  3371. win->window_width = screen_rect.size.width;
  3372. win->window_height = screen_rect.size.height;
  3373. }
  3374. NSUInteger style = NSWindowStyleMaskMiniaturizable;
  3375. if (win->desc.no_decoration) {
  3376. style |= NSWindowStyleMaskBorderless;
  3377. }
  3378. else {
  3379. style |= NSWindowStyleMaskTitled | NSWindowStyleMaskClosable | NSWindowStyleMaskResizable;
  3380. }
  3381. const NSRect content_rect = NSMakeRect(0, 0, win->window_width, win->window_height);
  3382. win->macos.window = [[_sapp_macos_window alloc]
  3383. initWithContentRect:content_rect
  3384. styleMask:style
  3385. backing:NSBackingStoreBuffered
  3386. defer:NO];
  3387. win->macos.window.win_id = win->slot.id;
  3388. win->macos.window.releasedWhenClosed = NO; // this is necessary for proper cleanup in applicationWillTerminate
  3389. win->macos.window.title = [NSString stringWithUTF8String:win->title];
  3390. win->macos.window.acceptsMouseMovedEvents = YES;
  3391. win->macos.window.restorable = YES;
  3392. win->macos.delegate = [[_sapp_macos_window_delegate alloc] init];
  3393. win->macos.delegate.win_id = win->slot.id;
  3394. win->macos.window.delegate = win->macos.delegate;
  3395. #if defined(SOKOL_METAL)
  3396. win->macos.view = [[_sapp_macos_view alloc] init];
  3397. win->macos.view.win_id = win->slot.id;
  3398. [win->macos.view updateTrackingAreas];
  3399. win->macos.view.preferredFramesPerSecond = 60 / win->desc.swap_interval;
  3400. win->macos.view.paused = YES;
  3401. win->macos.view.enableSetNeedsDisplay = NO;
  3402. win->macos.view.device = _sapp.macos.mtl_device;
  3403. win->macos.view.colorPixelFormat = MTLPixelFormatBGRA8Unorm;
  3404. win->macos.view.depthStencilPixelFormat = MTLPixelFormatDepth32Float_Stencil8;
  3405. win->macos.view.sampleCount = (NSUInteger) win->desc.sample_count;
  3406. win->macos.view.autoResizeDrawable = NO;
  3407. win->macos.window.contentView = win->macos.view;
  3408. [win->macos.window makeFirstResponder:win->macos.view];
  3409. win->macos.view.layer.magnificationFilter = kCAFilterNearest;
  3410. #elif defined(SOKOL_GLCORE33)
  3411. NSOpenGLPixelFormatAttribute attrs[32];
  3412. int i = 0;
  3413. attrs[i++] = NSOpenGLPFAAccelerated;
  3414. attrs[i++] = NSOpenGLPFADoubleBuffer;
  3415. attrs[i++] = NSOpenGLPFAOpenGLProfile; attrs[i++] = NSOpenGLProfileVersion3_2Core;
  3416. attrs[i++] = NSOpenGLPFAColorSize; attrs[i++] = 24;
  3417. attrs[i++] = NSOpenGLPFAAlphaSize; attrs[i++] = 8;
  3418. attrs[i++] = NSOpenGLPFADepthSize; attrs[i++] = 24;
  3419. attrs[i++] = NSOpenGLPFAStencilSize; attrs[i++] = 8;
  3420. if (win->desc.sample_count > 1) {
  3421. attrs[i++] = NSOpenGLPFAMultisample;
  3422. attrs[i++] = NSOpenGLPFASampleBuffers; attrs[i++] = 1;
  3423. attrs[i++] = NSOpenGLPFASamples; attrs[i++] = (NSOpenGLPixelFormatAttribute)_sapp.desc.sample_count;
  3424. }
  3425. else {
  3426. attrs[i++] = NSOpenGLPFASampleBuffers; attrs[i++] = 0;
  3427. }
  3428. attrs[i++] = 0;
  3429. NSOpenGLPixelFormat* nsglpixelformat_obj = [[NSOpenGLPixelFormat alloc] initWithAttributes:attrs];
  3430. SOKOL_ASSERT(nsglpixelformat_obj != nil);
  3431. NSOpenGLContext* shared_main_context = nil;
  3432. if (SAPP_INVALID_ID != _sapp.main_window_id) {
  3433. _sapp_window_t* main_win = _sapp_lookup_window(_sapp.main_window_id);
  3434. shared_main_context = main_win->macos.view.openGLContext;
  3435. }
  3436. win->macos.view = [[_sapp_macos_view alloc]
  3437. initWithFrame:content_rect
  3438. pixelFormat:nsglpixelformat_obj];
  3439. win->macos.view.win_id = win->slot.id;
  3440. [win->macos.view updateTrackingAreas];
  3441. if (win->desc.high_dpi) {
  3442. [win->macos.view setWantsBestResolutionOpenGLSurface:YES];
  3443. }
  3444. else {
  3445. [win->macos.view setWantsBestResolutionOpenGLSurface:NO];
  3446. }
  3447. if (nil != shared_main_context) {
  3448. // create a new NSOpenContext which shares resources with the main window's context
  3449. NSOpenGLContext* nsgl_context = [[NSOpenGLContext alloc] initWithFormat:nsglpixelformat_obj shareContext:shared_main_context];
  3450. SOKOL_ASSERT(nsgl_context);
  3451. win->macos.view.openGLContext = nsgl_context;
  3452. _SAPP_OBJC_RELEASE(nsgl_context);
  3453. }
  3454. _SAPP_OBJC_RELEASE(nsglpixelformat_obj);
  3455. win->macos.window.contentView = win->macos.view;
  3456. [win->macos.window makeFirstResponder:win->macos.view];
  3457. #endif
  3458. if (win->fullscreen) {
  3459. /* on GL, this already toggles a rendered frame, so set the valid flag before */
  3460. [win->macos.window toggleFullScreen:NSApp];
  3461. }
  3462. else {
  3463. if ((win->pos_x == 0) && (win->pos_y == 0)) {
  3464. [win->macos.window center];
  3465. }
  3466. else {
  3467. _sapp_macos_set_window_pos(win, win->pos_x, win->pos_y);
  3468. }
  3469. }
  3470. _sapp_macos_update_dimensions(win);
  3471. if (!win->desc.hidden) {
  3472. [win->macos.window makeKeyAndOrderFront:nil];
  3473. }
  3474. return true;
  3475. }
  3476. _SOKOL_PRIVATE void _sapp_macos_destroy_window(_sapp_window_t* win) {
  3477. SOKOL_ASSERT(win);
  3478. // NOTE: it's safe to call [release] on a nil object
  3479. _SAPP_OBJC_RELEASE(win->macos.tracking_area);
  3480. _SAPP_OBJC_RELEASE(win->macos.delegate);
  3481. _SAPP_OBJC_RELEASE(win->macos.view);
  3482. _SAPP_OBJC_RELEASE(win->macos.window);
  3483. }
  3484. _SOKOL_PRIVATE void _sapp_macos_close_window(_sapp_window_t* win) {
  3485. SOKOL_ASSERT(win && win->macos.window);
  3486. [win->macos.window close];
  3487. }
  3488. _SOKOL_PRIVATE void _sapp_macos_show_window(_sapp_window_t* win) {
  3489. SOKOL_ASSERT(win && win->macos.window);
  3490. [win->macos.window orderFront:nil];
  3491. }
  3492. _SOKOL_PRIVATE void _sapp_macos_hide_window(_sapp_window_t* win) {
  3493. SOKOL_ASSERT(win && win->macos.window);
  3494. [win->macos.window orderOut:nil];
  3495. }
  3496. _SOKOL_PRIVATE bool _sapp_macos_window_visible(_sapp_window_t* win) {
  3497. SOKOL_ASSERT(win && win->macos.window);
  3498. return [win->macos.window isVisible];
  3499. }
  3500. _SOKOL_PRIVATE void _sapp_macos_focus_window(_sapp_window_t* win) {
  3501. SOKOL_ASSERT(win && win->macos.window);
  3502. // FIXME: see the activateIgnoringOtherApps hack in GLFW, relevant for us?
  3503. [win->macos.window makeKeyAndOrderFront:nil];
  3504. }
  3505. _SOKOL_PRIVATE bool _sapp_macos_window_focused(_sapp_window_t* win) {
  3506. SOKOL_ASSERT(win && win->macos.window);
  3507. return [win->macos.window isKeyWindow];
  3508. }
  3509. _SOKOL_PRIVATE bool _sapp_macos_window_minimized(_sapp_window_t* win) {
  3510. SOKOL_ASSERT(win && win->macos.window);
  3511. return [win->macos.window isMiniaturized];
  3512. }
  3513. /* MacOS entry function */
  3514. #if !defined(SOKOL_NO_ENTRY)
  3515. int main(int argc, char* argv[]) {
  3516. sapp_desc desc = sokol_main(argc, argv);
  3517. _sapp_macos_run(&desc);
  3518. return 0;
  3519. }
  3520. #endif /* SOKOL_NO_ENTRY */
  3521. _SOKOL_PRIVATE uint32_t _sapp_macos_mods(NSEvent* ev) {
  3522. const NSEventModifierFlags f = ev.modifierFlags;
  3523. const NSUInteger b = NSEvent.pressedMouseButtons;
  3524. uint32_t m = 0;
  3525. if (f & NSEventModifierFlagShift) {
  3526. m |= SAPP_MODIFIER_SHIFT;
  3527. }
  3528. if (f & NSEventModifierFlagControl) {
  3529. m |= SAPP_MODIFIER_CTRL;
  3530. }
  3531. if (f & NSEventModifierFlagOption) {
  3532. m |= SAPP_MODIFIER_ALT;
  3533. }
  3534. if (f & NSEventModifierFlagCommand) {
  3535. m |= SAPP_MODIFIER_SUPER;
  3536. }
  3537. if (0 != (b & (1<<0))) {
  3538. m |= SAPP_MODIFIER_LMB;
  3539. }
  3540. if (0 != (b & (1<<1))) {
  3541. m |= SAPP_MODIFIER_RMB;
  3542. }
  3543. if (0 != (b & (1<<2))) {
  3544. m |= SAPP_MODIFIER_MMB;
  3545. }
  3546. return m;
  3547. }
  3548. _SOKOL_PRIVATE void _sapp_macos_mouse_event(_sapp_window_t* win, sapp_event_type type, sapp_mousebutton btn, uint32_t mod) {
  3549. if (_sapp_events_enabled()) {
  3550. _sapp_init_event(win, type);
  3551. _sapp.event.mouse_button = btn;
  3552. _sapp.event.modifiers = mod;
  3553. _sapp_call_event(&_sapp.event);
  3554. }
  3555. }
  3556. _SOKOL_PRIVATE void _sapp_macos_key_event(_sapp_window_t* win, sapp_event_type type, sapp_keycode key, bool repeat, uint32_t mod) {
  3557. if (_sapp_events_enabled()) {
  3558. _sapp_init_event(win, type);
  3559. _sapp.event.key_code = key;
  3560. _sapp.event.key_repeat = repeat;
  3561. _sapp.event.modifiers = mod;
  3562. _sapp_call_event(&_sapp.event);
  3563. }
  3564. }
  3565. _SOKOL_PRIVATE int _sapp_macos_flipy(_sapp_window_t* win, int y) {
  3566. SOKOL_ASSERT(win && (win->macos.window != nil));
  3567. NSRect screen_rect = win->macos.window.screen.frame;
  3568. return screen_rect.size.height - y - 1;
  3569. }
  3570. _SOKOL_PRIVATE void _sapp_macos_set_window_pos(_sapp_window_t* win, int x, int y) {
  3571. const NSRect content_rect = [win->macos.window frame];
  3572. const NSRect dummy_rect = NSMakeRect(x, _sapp_macos_flipy(win, y + content_rect.size.height - 1), 0, 0);
  3573. const NSRect frame_rect = [win->macos.window frameRectForContentRect:dummy_rect];
  3574. [win->macos.window setFrameOrigin:frame_rect.origin];
  3575. }
  3576. _SOKOL_PRIVATE void _sapp_macos_get_window_pos(_sapp_window_t* win, int* x, int* y) {
  3577. const NSRect content_rect = [win->macos.window contentRectForFrameRect:[win->macos.window frame]];
  3578. if (x) {
  3579. *x = content_rect.origin.x;
  3580. }
  3581. if (y) {
  3582. *y = _sapp_macos_flipy(win, content_rect.origin.y + content_rect.size.height - 1);
  3583. }
  3584. }
  3585. _SOKOL_PRIVATE void _sapp_macos_set_window_size(_sapp_window_t* win, int w, int h) {
  3586. NSRect content_rect = [win->macos.window contentRectForFrameRect:[win->macos.window frame]];
  3587. content_rect.origin.y += content_rect.size.height - h;
  3588. content_rect.size = NSMakeSize(w, h);
  3589. [win->macos.window setFrame:[win->macos.window frameRectForContentRect:content_rect] display:YES];
  3590. }
  3591. _SOKOL_PRIVATE void _sapp_macos_get_window_size(_sapp_window_t* win, int* w, int* h) {
  3592. const NSRect content_rect = [win->macos.window frame];
  3593. if (w) {
  3594. *w = content_rect.size.width;
  3595. }
  3596. if (h) {
  3597. *h = content_rect.size.height;
  3598. }
  3599. }
  3600. _SOKOL_PRIVATE void _sapp_macos_update_window_position(_sapp_window_t* win) {
  3601. const NSRect content_rect = [win->macos.window contentRectForFrameRect:[win->macos.window frame]];
  3602. win->pos_x = content_rect.origin.x;
  3603. win->pos_y = _sapp_macos_flipy(win, content_rect.origin.y);
  3604. }
  3605. _SOKOL_PRIVATE void _sapp_macos_activate_window_context(_sapp_window_t* win) {
  3606. #if defined(SOKOL_GLCORE33)
  3607. NSOpenGLContext* ctx = [win->macos.view openGLContext];
  3608. [ctx makeCurrentContext];
  3609. #else
  3610. _SOKOL_UNUSED(win);
  3611. #endif
  3612. }
  3613. _SOKOL_PRIVATE void _sapp_macos_toggle_fullscreen(_sapp_window_t* win) {
  3614. /* NOTE: the _sapp.fullscreen flag is also notified by the
  3615. windowDidEnterFullscreen / windowDidExitFullscreen
  3616. event handlers
  3617. */
  3618. win->fullscreen = !win->fullscreen;
  3619. [win->macos.window toggleFullScreen:nil];
  3620. }
  3621. _SOKOL_PRIVATE void _sapp_macos_set_clipboard_string(const char* str) {
  3622. NSPasteboard* pasteboard = [NSPasteboard generalPasteboard];
  3623. [pasteboard declareTypes:@[NSPasteboardTypeString] owner:nil];
  3624. [pasteboard setString:@(str) forType:NSPasteboardTypeString];
  3625. }
  3626. _SOKOL_PRIVATE const char* _sapp_macos_get_clipboard_string(void) {
  3627. SOKOL_ASSERT(_sapp.clipboard.buffer);
  3628. _sapp.clipboard.buffer[0] = 0;
  3629. NSPasteboard* pasteboard = [NSPasteboard generalPasteboard];
  3630. if (![[pasteboard types] containsObject:NSPasteboardTypeString]) {
  3631. return _sapp.clipboard.buffer;
  3632. }
  3633. NSString* str = [pasteboard stringForType:NSPasteboardTypeString];
  3634. if (!str) {
  3635. return _sapp.clipboard.buffer;
  3636. }
  3637. _sapp_strcpy([str UTF8String], _sapp.clipboard.buffer, _sapp.clipboard.buf_size);
  3638. return _sapp.clipboard.buffer;
  3639. }
  3640. _SOKOL_PRIVATE void _sapp_macos_update_window_title(_sapp_window_t* win) {
  3641. [win->macos.window setTitle: [NSString stringWithUTF8String:win->title]];
  3642. }
  3643. _SOKOL_PRIVATE void _sapp_macos_update_mouse(_sapp_window_t* win, NSEvent* event) {
  3644. if (!win->mouse.locked) {
  3645. const NSRect content_rect = [win->macos.window contentRectForFrameRect:[win->macos.window frame]];
  3646. const NSPoint mouse_pos = event.locationInWindow;
  3647. float new_x = mouse_pos.x * win->dpi_scale;
  3648. float new_y = content_rect.size.height - (mouse_pos.y * win->dpi_scale) - 1;
  3649. /* don't update dx/dy in the very first update */
  3650. if (win->mouse.pos_valid) {
  3651. win->mouse.dx = new_x - win->mouse.x;
  3652. win->mouse.dy = new_y - win->mouse.y;
  3653. }
  3654. win->mouse.x = new_x;
  3655. win->mouse.y = new_y;
  3656. win->mouse.pos_valid = true;
  3657. }
  3658. }
  3659. _SOKOL_PRIVATE void _sapp_macos_update_mouse_delta(_sapp_window_t* win, NSEvent* event) {
  3660. if (win->mouse.locked) {
  3661. win->mouse.dx = [event deltaX];
  3662. win->mouse.dy = [event deltaY];
  3663. }
  3664. }
  3665. _SOKOL_PRIVATE void _sapp_macos_show_mouse(bool visible) {
  3666. /* NOTE: this function is only called when the mouse visibility actually changes */
  3667. if (visible) {
  3668. CGDisplayShowCursor(kCGDirectMainDisplay);
  3669. }
  3670. else {
  3671. CGDisplayHideCursor(kCGDirectMainDisplay);
  3672. }
  3673. }
  3674. _SOKOL_PRIVATE void _sapp_macos_lock_mouse(_sapp_window_t* win, bool lock) {
  3675. if (lock == win->mouse.locked) {
  3676. return;
  3677. }
  3678. win->mouse.dx = 0.0f;
  3679. win->mouse.dy = 0.0f;
  3680. win->mouse.locked = lock;
  3681. /*
  3682. NOTE that this code doesn't warp the mouse cursor to the window
  3683. center as everybody else does it. This lead to a spike in the
  3684. *second* mouse-moved event after the warp happened. The
  3685. mouse centering doesn't seem to be required (mouse-moved events
  3686. are reported correctly even when the cursor is at an edge of the screen).
  3687. NOTE also that the hide/show of the mouse cursor should properly
  3688. stack with calls to sapp_show_mouse()
  3689. */
  3690. if (win->mouse.locked) {
  3691. CGAssociateMouseAndMouseCursorPosition(NO);
  3692. CGDisplayHideCursor(kCGDirectMainDisplay);
  3693. }
  3694. else {
  3695. CGDisplayShowCursor(kCGDirectMainDisplay);
  3696. CGAssociateMouseAndMouseCursorPosition(YES);
  3697. }
  3698. }
  3699. _SOKOL_PRIVATE void _sapp_macos_set_icon(const sapp_icon_desc* icon_desc, int num_images) {
  3700. NSDockTile* dock_tile = NSApp.dockTile;
  3701. const int wanted_width = (int) dock_tile.size.width;
  3702. const int wanted_height = (int) dock_tile.size.height;
  3703. const int img_index = _sapp_image_bestmatch(icon_desc->images, num_images, wanted_width, wanted_height);
  3704. const sapp_image_desc* img_desc = &icon_desc->images[img_index];
  3705. CGColorSpaceRef cg_color_space = CGColorSpaceCreateDeviceRGB();
  3706. CFDataRef cf_data = CFDataCreate(kCFAllocatorDefault, (const UInt8*)img_desc->pixels.ptr, (CFIndex)img_desc->pixels.size);
  3707. CGDataProviderRef cg_data_provider = CGDataProviderCreateWithCFData(cf_data);
  3708. CGImageRef cg_img = CGImageCreate(
  3709. (size_t)img_desc->width, // width
  3710. (size_t)img_desc->height, // height
  3711. 8, // bitsPerComponent
  3712. 32, // bitsPerPixel
  3713. (size_t)img_desc->width * 4,// bytesPerRow
  3714. cg_color_space, // space
  3715. kCGImageAlphaLast | kCGImageByteOrderDefault, // bitmapInfo
  3716. cg_data_provider, // provider
  3717. NULL, // decode
  3718. false, // shouldInterpolate
  3719. kCGRenderingIntentDefault);
  3720. CFRelease(cf_data);
  3721. CGDataProviderRelease(cg_data_provider);
  3722. CGColorSpaceRelease(cg_color_space);
  3723. NSImage* ns_image = [[NSImage alloc] initWithCGImage:cg_img size:dock_tile.size];
  3724. dock_tile.contentView = [NSImageView imageViewWithImage:ns_image];
  3725. [dock_tile display];
  3726. _SAPP_OBJC_RELEASE(ns_image);
  3727. CGImageRelease(cg_img);
  3728. }
  3729. _SOKOL_PRIVATE CVReturn _sapp_macos_displaylink_callback(
  3730. CVDisplayLinkRef displayLink,
  3731. const CVTimeStamp* now,
  3732. const CVTimeStamp* outputTime,
  3733. CVOptionFlags flagsIn,
  3734. CVOptionFlags* flagsOut,
  3735. void* displayLinkContext)
  3736. {
  3737. _SOKOL_UNUSED(displayLink);
  3738. _SOKOL_UNUSED(now);
  3739. _SOKOL_UNUSED(outputTime);
  3740. _SOKOL_UNUSED(flagsIn);
  3741. _SOKOL_UNUSED(flagsOut);
  3742. dispatch_source_t source = (__bridge dispatch_source_t) displayLinkContext;
  3743. dispatch_source_merge_data(source, 1);
  3744. return kCVReturnSuccess;
  3745. }
  3746. @implementation _sapp_macos_app_delegate
  3747. - (void)drawFrame {
  3748. #if defined(SOKOL_GLCORE33)
  3749. _sapp_window_t* win = _sapp_lookup_window(_sapp.main_window_id);
  3750. _sapp_macos_activate_window_context(win);
  3751. #endif
  3752. /* NOTE: the MTKView drawables MUST be resized right before the dummy draw
  3753. invocation, *NOT* from within the event loop, this is because
  3754. MTKView lazily resizes some "secondary" surfaces at the start
  3755. of the draw method before drawRect callback is called. Setting
  3756. the drawableSize in the event loop causes currentRenderPassDescriptor
  3757. to return a render pass descriptor with different surfaces sizes
  3758. */
  3759. for (int i = 0; i < _sapp.window_pool.pool.size; i++) {
  3760. const uint32_t win_id = _sapp.window_pool.windows[i].slot.id;
  3761. _sapp_window_t* win = _sapp_lookup_window(win_id);
  3762. if (win) {
  3763. _sapp_macos_update_dimensions(win);
  3764. // let MTKView lazily resize its framebuffer textures
  3765. #if defined(SOKOL_METAL)
  3766. [win->macos.view draw];
  3767. #endif
  3768. }
  3769. }
  3770. _sapp_frame();
  3771. #if defined(SOKOL_GLCORE33)
  3772. for (int i = 0; i < _sapp.window_pool.pool.size; i++) {
  3773. const uint32_t win_id = _sapp.window_pool.windows[i].slot.id;
  3774. _sapp_window_t* win = _sapp_lookup_window(win_id);
  3775. if (win) {
  3776. [[win->macos.view openGLContext] flushBuffer];
  3777. }
  3778. }
  3779. #endif
  3780. if (_sapp.quit_requested && !_sapp.quit_ordered) {
  3781. _sapp_window_t* win = _sapp_lookup_window(_sapp.main_window_id);
  3782. _sapp_macos_close_window(win);
  3783. }
  3784. if (_sapp.quit_ordered) {
  3785. [NSApp terminate:self];
  3786. }
  3787. }
  3788. - (void)applicationDidFinishLaunching:(NSNotification*)aNotification {
  3789. _SOKOL_UNUSED(aNotification);
  3790. const sapp_window_desc window_desc = _sapp_desc_to_window_desc(&_sapp.desc);
  3791. _sapp.main_window_id = _sapp_create_window(&window_desc);
  3792. NSApp.activationPolicy = NSApplicationActivationPolicyRegular;
  3793. [NSApp activateIgnoringOtherApps:YES];
  3794. [NSEvent setMouseCoalescingEnabled:NO];
  3795. _sapp_window_t* win = _sapp_lookup_window(_sapp.main_window_id);
  3796. SOKOL_ASSERT(win);
  3797. _sapp_macos_update_dimensions(win);
  3798. // setup display link
  3799. // see: https://developer.apple.com/documentation/metal/drawable_objects/creating_a_custom_metal_view?language=objc
  3800. _sapp.macos.display_source = dispatch_source_create(DISPATCH_SOURCE_TYPE_DATA_ADD, 0, 0, dispatch_get_main_queue());
  3801. dispatch_source_set_event_handler(_sapp.macos.display_source, ^(){
  3802. @autoreleasepool {
  3803. [_sapp.macos.app_delegate drawFrame];
  3804. }
  3805. });
  3806. dispatch_resume(_sapp.macos.display_source);
  3807. CVDisplayLinkCreateWithActiveCGDisplays(&_sapp.macos.display_link);
  3808. CVDisplayLinkSetOutputCallback(_sapp.macos.display_link, &_sapp_macos_displaylink_callback, (__bridge void*)_sapp.macos.display_source);
  3809. CGDirectDisplayID disp_id = (CGDirectDisplayID) [NSScreen.mainScreen.deviceDescription[@"NSScreenNumber"] unsignedIntegerValue];
  3810. CVDisplayLinkSetCurrentCGDisplay(_sapp.macos.display_link, disp_id);
  3811. CVDisplayLinkStart(_sapp.macos.display_link);
  3812. _sapp.valid = true;
  3813. }
  3814. - (void)applicationWillTerminate:(NSNotification*)notification {
  3815. _SOKOL_UNUSED(notification);
  3816. _sapp_discard_state();
  3817. }
  3818. - (BOOL)applicationShouldHandleReopen:(NSApplication*)sender hasVisibleWindows:(BOOL)flag {
  3819. _SOKOL_UNUSED(sender);
  3820. _SOKOL_UNUSED(flag);
  3821. /* unminimize the main window when clicking the dock icon (which doesn't
  3822. happen automatically if the application has multiple open windows)
  3823. */
  3824. _sapp_window_t* win = _sapp_lookup_window(_sapp.main_window_id);
  3825. SOKOL_ASSERT(win);
  3826. [win->macos.window makeKeyAndOrderFront:nil];
  3827. return YES;
  3828. }
  3829. @end
  3830. @implementation _sapp_macos_window_delegate
  3831. @synthesize win_id;
  3832. - (BOOL)windowShouldClose:(id)sender {
  3833. _SOKOL_UNUSED(sender);
  3834. if (self.win_id == _sapp.main_window_id) {
  3835. /* only give user-code a chance to intervene when sapp_quit() wasn't already called */
  3836. if (!_sapp.quit_ordered) {
  3837. /* if window should be closed and event handling is enabled, give user code
  3838. a chance to intervene via sapp_cancel_quit()
  3839. */
  3840. _sapp.quit_requested = true;
  3841. _sapp_window_t* win = _sapp_lookup_window(self.win_id);
  3842. SOKOL_ASSERT(win);
  3843. _sapp_macos_app_event(win, SAPP_EVENTTYPE_QUIT_REQUESTED);
  3844. /* user code hasn't intervened, quit the app */
  3845. if (_sapp.quit_requested) {
  3846. _sapp.quit_ordered = true;
  3847. }
  3848. }
  3849. if (_sapp.quit_ordered) {
  3850. return YES;
  3851. }
  3852. else {
  3853. return NO;
  3854. }
  3855. }
  3856. else {
  3857. // not the main window
  3858. return YES;
  3859. }
  3860. }
  3861. - (void)windowWillClose:(id)sender {
  3862. _SOKOL_UNUSED(sender);
  3863. // the main window will be closed when the application closes
  3864. if (self.win_id != _sapp.main_window_id) {
  3865. _sapp_window_t* win = _sapp_lookup_window(self.win_id);
  3866. if (win) {
  3867. // destroy state associated with the window
  3868. _sapp_destroy_window(self.win_id);
  3869. }
  3870. }
  3871. }
  3872. - (void)windowDidMiniaturize:(NSNotification*)notification {
  3873. _SOKOL_UNUSED(notification);
  3874. _sapp_window_t* win = _sapp_lookup_window(self.win_id);
  3875. if (win) {
  3876. _sapp_macos_app_event(win, SAPP_EVENTTYPE_ICONIFIED);
  3877. }
  3878. }
  3879. - (void)windowDidDeminiaturize:(NSNotification*)notification {
  3880. _SOKOL_UNUSED(notification);
  3881. _sapp_window_t* win = _sapp_lookup_window(self.win_id);
  3882. if (win) {
  3883. _sapp_macos_app_event(win, SAPP_EVENTTYPE_RESTORED);
  3884. }
  3885. }
  3886. - (void)windowDidEnterFullScreen:(NSNotification*)notification {
  3887. _SOKOL_UNUSED(notification);
  3888. _sapp_window_t* win = _sapp_lookup_window(self.win_id);
  3889. if (win) {
  3890. win->fullscreen = true;
  3891. }
  3892. }
  3893. - (void)windowDidExitFullScreen:(NSNotification*)notification {
  3894. _SOKOL_UNUSED(notification);
  3895. _sapp_window_t* win = _sapp_lookup_window(self.win_id);
  3896. if (win) {
  3897. win->fullscreen = false;
  3898. }
  3899. }
  3900. @end
  3901. @implementation _sapp_macos_window
  3902. @synthesize win_id;
  3903. - (instancetype)initWithContentRect:(NSRect)contentRect
  3904. styleMask:(NSWindowStyleMask)style
  3905. backing:(NSBackingStoreType)backingStoreType
  3906. defer:(BOOL)flag {
  3907. if (self = [super initWithContentRect:contentRect styleMask:style backing:backingStoreType defer:flag]) {
  3908. #if __MAC_OS_X_VERSION_MAX_ALLOWED >= 101300
  3909. [self registerForDraggedTypes:[NSArray arrayWithObject:NSPasteboardTypeFileURL]];
  3910. #endif
  3911. }
  3912. return self;
  3913. }
  3914. - (NSDragOperation)draggingEntered:(id<NSDraggingInfo>)sender {
  3915. return NSDragOperationCopy;
  3916. }
  3917. - (NSDragOperation)draggingUpdated:(id<NSDraggingInfo>)sender {
  3918. return NSDragOperationCopy;
  3919. }
  3920. - (BOOL)performDragOperation:(id<NSDraggingInfo>)sender {
  3921. BOOL retval = NO;
  3922. #if __MAC_OS_X_VERSION_MAX_ALLOWED >= 101300
  3923. _sapp_window_t* win = _sapp_lookup_window(self.win_id);
  3924. SOKOL_ASSERT(win);
  3925. NSPasteboard *pboard = [sender draggingPasteboard];
  3926. if ([pboard.types containsObject:NSPasteboardTypeFileURL]) {
  3927. _sapp_clear_drop_buffer(&_sapp.drop);
  3928. _sapp.drop.num_files = ((int)pboard.pasteboardItems.count > _sapp.drop.max_files) ? _sapp.drop.max_files : pboard.pasteboardItems.count;
  3929. bool drop_failed = false;
  3930. for (int i = 0; i < _sapp.drop.num_files; i++) {
  3931. NSURL *fileUrl = [NSURL fileURLWithPath:[pboard.pasteboardItems[(NSUInteger)i] stringForType:NSPasteboardTypeFileURL]];
  3932. if (!_sapp_strcpy(fileUrl.standardizedURL.path.UTF8String, _sapp_dropped_file_path_ptr(i), _sapp.drop.max_path_length)) {
  3933. SOKOL_LOG("sokol_app.h: dropped file path too long (sapp_desc.max_dropped_file_path_length)\n");
  3934. drop_failed = true;
  3935. break;
  3936. }
  3937. }
  3938. if (!drop_failed) {
  3939. if (_sapp_events_enabled()) {
  3940. _sapp_init_event(win, SAPP_EVENTTYPE_FILES_DROPPED);
  3941. _sapp_call_event(&_sapp.event);
  3942. }
  3943. }
  3944. else {
  3945. _sapp_clear_drop_buffer(&_sapp.drop);
  3946. _sapp.drop.num_files = 0;
  3947. }
  3948. retval = YES;
  3949. }
  3950. #endif
  3951. return retval;
  3952. }
  3953. @end
  3954. @implementation _sapp_macos_view
  3955. @synthesize win_id;
  3956. #if defined(SOKOL_GLCORE33)
  3957. /* NOTE: this is a hack/fix when the initial window size has been clipped by
  3958. macOS because it didn't fit on the screen, in that case the
  3959. frame size of the window is reported wrong if low-dpi rendering
  3960. was requested (instead the high-dpi dimensions are returned)
  3961. until the window is resized for the first time.
  3962. Hooking into reshape and getting the frame dimensions seems to report
  3963. the correct dimensions.
  3964. */
  3965. - (void)reshape {
  3966. [super reshape];
  3967. _sapp_window_t* win = _sapp_lookup_window(self.win_id);
  3968. if (win) {
  3969. _sapp_macos_update_dimensions(win);
  3970. }
  3971. }
  3972. - (void)prepareOpenGL {
  3973. [super prepareOpenGL];
  3974. GLint swapInt = 1;
  3975. _sapp_window_t* win = _sapp_lookup_window(self.win_id);
  3976. if (win) {
  3977. NSOpenGLContext* ctx = [win->macos.view openGLContext];
  3978. [ctx setValues:&swapInt forParameter:NSOpenGLContextParameterSwapInterval];
  3979. [ctx makeCurrentContext];
  3980. }
  3981. }
  3982. #endif
  3983. - (void)drawRect:(NSRect)rect {
  3984. _SOKOL_UNUSED(rect);
  3985. }
  3986. - (BOOL)isOpaque {
  3987. return YES;
  3988. }
  3989. - (BOOL)canBecomeKeyView {
  3990. return YES;
  3991. }
  3992. - (BOOL)acceptsFirstResponder {
  3993. return YES;
  3994. }
  3995. - (void)updateTrackingAreas {
  3996. _sapp_window_t* win = _sapp_lookup_window(self.win_id);
  3997. SOKOL_ASSERT(win);
  3998. if (win->macos.tracking_area != nil) {
  3999. [self removeTrackingArea:win->macos.tracking_area];
  4000. _SAPP_OBJC_RELEASE(win->macos.tracking_area);
  4001. }
  4002. const NSTrackingAreaOptions options = NSTrackingMouseEnteredAndExited |
  4003. NSTrackingActiveInKeyWindow |
  4004. NSTrackingEnabledDuringMouseDrag |
  4005. NSTrackingCursorUpdate |
  4006. NSTrackingInVisibleRect |
  4007. NSTrackingAssumeInside;
  4008. win->macos.tracking_area = [[NSTrackingArea alloc] initWithRect:[self bounds] options:options owner:self userInfo:nil];
  4009. [self addTrackingArea:win->macos.tracking_area];
  4010. [super updateTrackingAreas];
  4011. }
  4012. - (void)mouseEntered:(NSEvent*)event {
  4013. _sapp_window_t* win = _sapp_lookup_window(self.win_id);
  4014. if (win) {
  4015. _sapp_macos_update_mouse(win, event);
  4016. /* don't send mouse enter/leave while dragging (so that it behaves the same as
  4017. on Windows while SetCapture is active
  4018. */
  4019. if (0 == win->macos.mouse_buttons) {
  4020. _sapp_macos_mouse_event(win, SAPP_EVENTTYPE_MOUSE_ENTER, SAPP_MOUSEBUTTON_INVALID, _sapp_macos_mods(event));
  4021. }
  4022. }
  4023. }
  4024. - (void)mouseExited:(NSEvent*)event {
  4025. _sapp_window_t* win = _sapp_lookup_window(self.win_id);
  4026. if (win) {
  4027. _sapp_macos_update_mouse(win, event);
  4028. if (0 == win->macos.mouse_buttons) {
  4029. _sapp_macos_mouse_event(win, SAPP_EVENTTYPE_MOUSE_LEAVE, SAPP_MOUSEBUTTON_INVALID, _sapp_macos_mods(event));
  4030. }
  4031. }
  4032. }
  4033. - (void)mouseDown:(NSEvent*)event {
  4034. _sapp_window_t* win = _sapp_lookup_window(self.win_id);
  4035. if (win) {
  4036. _sapp_macos_update_mouse(win, event);
  4037. _sapp_macos_mouse_event(win, SAPP_EVENTTYPE_MOUSE_DOWN, SAPP_MOUSEBUTTON_LEFT, _sapp_macos_mods(event));
  4038. win->macos.mouse_buttons |= (1<<SAPP_MOUSEBUTTON_LEFT);
  4039. }
  4040. }
  4041. - (void)mouseUp:(NSEvent*)event {
  4042. _sapp_window_t* win = _sapp_lookup_window(self.win_id);
  4043. if (win) {
  4044. _sapp_macos_update_mouse(win, event);
  4045. _sapp_macos_mouse_event(win, SAPP_EVENTTYPE_MOUSE_UP, SAPP_MOUSEBUTTON_LEFT, _sapp_macos_mods(event));
  4046. win->macos.mouse_buttons &= ~(1<<SAPP_MOUSEBUTTON_LEFT);
  4047. }
  4048. }
  4049. - (void)rightMouseDown:(NSEvent*)event {
  4050. _sapp_window_t* win = _sapp_lookup_window(self.win_id);
  4051. if (win) {
  4052. _sapp_macos_update_mouse(win, event);
  4053. _sapp_macos_mouse_event(win, SAPP_EVENTTYPE_MOUSE_DOWN, SAPP_MOUSEBUTTON_RIGHT, _sapp_macos_mods(event));
  4054. win->macos.mouse_buttons |= (1<<SAPP_MOUSEBUTTON_RIGHT);
  4055. }
  4056. }
  4057. - (void)rightMouseUp:(NSEvent*)event {
  4058. _sapp_window_t* win = _sapp_lookup_window(self.win_id);
  4059. if (win) {
  4060. _sapp_macos_update_mouse(win, event);
  4061. _sapp_macos_mouse_event(win, SAPP_EVENTTYPE_MOUSE_UP, SAPP_MOUSEBUTTON_RIGHT, _sapp_macos_mods(event));
  4062. win->macos.mouse_buttons &= ~(1<<SAPP_MOUSEBUTTON_RIGHT);
  4063. }
  4064. }
  4065. - (void)otherMouseDown:(NSEvent*)event {
  4066. _sapp_window_t* win = _sapp_lookup_window(self.win_id);
  4067. if (win) {
  4068. _sapp_macos_update_mouse(win, event);
  4069. if (2 == event.buttonNumber) {
  4070. _sapp_macos_mouse_event(win, SAPP_EVENTTYPE_MOUSE_DOWN, SAPP_MOUSEBUTTON_MIDDLE, _sapp_macos_mods(event));
  4071. win->macos.mouse_buttons |= (1<<SAPP_MOUSEBUTTON_MIDDLE);
  4072. }
  4073. }
  4074. }
  4075. - (void)otherMouseUp:(NSEvent*)event {
  4076. _sapp_window_t* win = _sapp_lookup_window(self.win_id);
  4077. if (win) {
  4078. _sapp_macos_update_mouse(win, event);
  4079. if (2 == event.buttonNumber) {
  4080. _sapp_macos_mouse_event(win, SAPP_EVENTTYPE_MOUSE_UP, SAPP_MOUSEBUTTON_MIDDLE, _sapp_macos_mods(event));
  4081. win->macos.mouse_buttons &= (1<<SAPP_MOUSEBUTTON_MIDDLE);
  4082. }
  4083. }
  4084. }
  4085. - (void)otherMouseDragged:(NSEvent*)event {
  4086. _sapp_window_t* win = _sapp_lookup_window(self.win_id);
  4087. if (win) {
  4088. _sapp_macos_update_mouse(win, event);
  4089. _sapp_macos_update_mouse_delta(win, event);
  4090. if (2 == event.buttonNumber) {
  4091. _sapp_macos_mouse_event(win, SAPP_EVENTTYPE_MOUSE_MOVE, SAPP_MOUSEBUTTON_INVALID, _sapp_macos_mods(event));
  4092. }
  4093. }
  4094. }
  4095. - (void)mouseMoved:(NSEvent*)event {
  4096. _sapp_window_t* win = _sapp_lookup_window(self.win_id);
  4097. if (win) {
  4098. _sapp_macos_update_mouse(win, event);
  4099. _sapp_macos_update_mouse_delta(win, event);
  4100. _sapp_macos_mouse_event(win, SAPP_EVENTTYPE_MOUSE_MOVE, SAPP_MOUSEBUTTON_INVALID , _sapp_macos_mods(event));
  4101. }
  4102. }
  4103. - (void)mouseDragged:(NSEvent*)event {
  4104. _sapp_window_t* win = _sapp_lookup_window(self.win_id);
  4105. if (win) {
  4106. _sapp_macos_update_mouse(win, event);
  4107. _sapp_macos_update_mouse_delta(win, event);
  4108. _sapp_macos_mouse_event(win, SAPP_EVENTTYPE_MOUSE_MOVE, SAPP_MOUSEBUTTON_INVALID , _sapp_macos_mods(event));
  4109. }
  4110. }
  4111. - (void)rightMouseDragged:(NSEvent*)event {
  4112. _sapp_window_t* win = _sapp_lookup_window(self.win_id);
  4113. if (win) {
  4114. _sapp_macos_update_mouse(win, event);
  4115. _sapp_macos_update_mouse_delta(win, event);
  4116. _sapp_macos_mouse_event(win, SAPP_EVENTTYPE_MOUSE_MOVE, SAPP_MOUSEBUTTON_INVALID, _sapp_macos_mods(event));
  4117. }
  4118. }
  4119. - (void)scrollWheel:(NSEvent*)event {
  4120. _sapp_window_t* win = _sapp_lookup_window(self.win_id);
  4121. if (win) {
  4122. _sapp_macos_update_mouse(win, event);
  4123. if (_sapp_events_enabled()) {
  4124. float dx = (float) event.scrollingDeltaX;
  4125. float dy = (float) event.scrollingDeltaY;
  4126. if (event.hasPreciseScrollingDeltas) {
  4127. dx *= 0.1;
  4128. dy *= 0.1;
  4129. }
  4130. if ((_sapp_absf(dx) > 0.0f) || (_sapp_absf(dy) > 0.0f)) {
  4131. _sapp_init_event(win, SAPP_EVENTTYPE_MOUSE_SCROLL);
  4132. _sapp.event.modifiers = _sapp_macos_mods(event);
  4133. _sapp.event.scroll_x = dx;
  4134. _sapp.event.scroll_y = dy;
  4135. _sapp_call_event(&_sapp.event);
  4136. }
  4137. }
  4138. }
  4139. }
  4140. - (void)keyDown:(NSEvent*)event {
  4141. _sapp_window_t* win = _sapp_lookup_window(self.win_id);
  4142. if (win) {
  4143. if (_sapp_events_enabled()) {
  4144. const uint32_t mods = _sapp_macos_mods(event);
  4145. /* NOTE: macOS doesn't send keyUp events while the Cmd key is pressed,
  4146. as a workaround, to prevent key presses from sticking we'll send
  4147. a keyup event following right after the keydown if SUPER is also pressed
  4148. */
  4149. const sapp_keycode key_code = _sapp_translate_key(event.keyCode);
  4150. _sapp_macos_key_event(win, SAPP_EVENTTYPE_KEY_DOWN, key_code, event.isARepeat, mods);
  4151. if (0 != (mods & SAPP_MODIFIER_SUPER)) {
  4152. _sapp_macos_key_event(win, SAPP_EVENTTYPE_KEY_UP, key_code, event.isARepeat, mods);
  4153. }
  4154. const NSString* chars = event.characters;
  4155. const NSUInteger len = chars.length;
  4156. if (len > 0) {
  4157. _sapp_init_event(win, SAPP_EVENTTYPE_CHAR);
  4158. _sapp.event.modifiers = mods;
  4159. for (NSUInteger i = 0; i < len; i++) {
  4160. const unichar codepoint = [chars characterAtIndex:i];
  4161. if ((codepoint & 0xFF00) == 0xF700) {
  4162. continue;
  4163. }
  4164. _sapp.event.char_code = codepoint;
  4165. _sapp.event.key_repeat = event.isARepeat;
  4166. _sapp_call_event(&_sapp.event);
  4167. }
  4168. }
  4169. /* if this is a Cmd+V (paste), also send a CLIPBOARD_PASTE event */
  4170. if (_sapp.clipboard.enabled && (mods == SAPP_MODIFIER_SUPER) && (key_code == SAPP_KEYCODE_V)) {
  4171. _sapp_init_event(win, SAPP_EVENTTYPE_CLIPBOARD_PASTED);
  4172. _sapp_call_event(&_sapp.event);
  4173. }
  4174. }
  4175. }
  4176. }
  4177. - (void)keyUp:(NSEvent*)event {
  4178. _sapp_window_t* win = _sapp_lookup_window(self.win_id);
  4179. if (win) {
  4180. _sapp_macos_key_event(win, SAPP_EVENTTYPE_KEY_UP,
  4181. _sapp_translate_key(event.keyCode),
  4182. event.isARepeat,
  4183. _sapp_macos_mods(event));
  4184. }
  4185. }
  4186. - (void)flagsChanged:(NSEvent*)event {
  4187. _sapp_window_t* win = _sapp_lookup_window(self.win_id);
  4188. if (win) {
  4189. const uint32_t old_f = win->macos.flags_changed_store;
  4190. const uint32_t new_f = event.modifierFlags;
  4191. win->macos.flags_changed_store = new_f;
  4192. sapp_keycode key_code = SAPP_KEYCODE_INVALID;
  4193. bool down = false;
  4194. if ((new_f ^ old_f) & NSEventModifierFlagShift) {
  4195. key_code = SAPP_KEYCODE_LEFT_SHIFT;
  4196. down = 0 != (new_f & NSEventModifierFlagShift);
  4197. }
  4198. if ((new_f ^ old_f) & NSEventModifierFlagControl) {
  4199. key_code = SAPP_KEYCODE_LEFT_CONTROL;
  4200. down = 0 != (new_f & NSEventModifierFlagControl);
  4201. }
  4202. if ((new_f ^ old_f) & NSEventModifierFlagOption) {
  4203. key_code = SAPP_KEYCODE_LEFT_ALT;
  4204. down = 0 != (new_f & NSEventModifierFlagOption);
  4205. }
  4206. if ((new_f ^ old_f) & NSEventModifierFlagCommand) {
  4207. key_code = SAPP_KEYCODE_LEFT_SUPER;
  4208. down = 0 != (new_f & NSEventModifierFlagCommand);
  4209. }
  4210. if (key_code != SAPP_KEYCODE_INVALID) {
  4211. _sapp_macos_key_event(win, down ? SAPP_EVENTTYPE_KEY_DOWN : SAPP_EVENTTYPE_KEY_UP,
  4212. key_code,
  4213. false,
  4214. _sapp_macos_mods(event));
  4215. }
  4216. }
  4217. }
  4218. - (void)cursorUpdate:(NSEvent*)event {
  4219. _SOKOL_UNUSED(event);
  4220. _sapp_window_t* win = _sapp_lookup_window(self.win_id);
  4221. if (win) {
  4222. if (win->desc.user_cursor) {
  4223. _sapp_macos_app_event(win, SAPP_EVENTTYPE_UPDATE_CURSOR);
  4224. }
  4225. }
  4226. }
  4227. @end
  4228. #endif /* MacOS */
  4229. /*== iOS =====================================================================*/
  4230. #if defined(_SAPP_IOS)
  4231. _SOKOL_PRIVATE void _sapp_ios_discard_state(void) {
  4232. // NOTE: it's safe to call [release] on a nil object
  4233. _SAPP_OBJC_RELEASE(_sapp.ios.textfield_dlg);
  4234. _SAPP_OBJC_RELEASE(_sapp.ios.textfield);
  4235. #if defined(SOKOL_METAL)
  4236. _SAPP_OBJC_RELEASE(_sapp.ios.view_ctrl);
  4237. _SAPP_OBJC_RELEASE(_sapp.ios.mtl_device);
  4238. #else
  4239. _SAPP_OBJC_RELEASE(_sapp.ios.view_ctrl);
  4240. _SAPP_OBJC_RELEASE(_sapp.ios.eagl_ctx);
  4241. #endif
  4242. _SAPP_OBJC_RELEASE(_sapp.ios.view);
  4243. _SAPP_OBJC_RELEASE(_sapp.ios.window);
  4244. }
  4245. _SOKOL_PRIVATE void _sapp_ios_run(const sapp_desc* desc) {
  4246. _sapp_init_state(desc);
  4247. static int argc = 1;
  4248. static char* argv[] = { (char*)"sokol_app" };
  4249. UIApplicationMain(argc, argv, nil, NSStringFromClass([_sapp_app_delegate class]));
  4250. }
  4251. /* iOS entry function */
  4252. #if !defined(SOKOL_NO_ENTRY)
  4253. int main(int argc, char* argv[]) {
  4254. sapp_desc desc = sokol_main(argc, argv);
  4255. _sapp_ios_run(&desc);
  4256. return 0;
  4257. }
  4258. #endif /* SOKOL_NO_ENTRY */
  4259. _SOKOL_PRIVATE void _sapp_ios_app_event(sapp_event_type type) {
  4260. if (_sapp_events_enabled()) {
  4261. _sapp_init_event(type);
  4262. _sapp_call_event(&_sapp.event);
  4263. }
  4264. }
  4265. _SOKOL_PRIVATE void _sapp_ios_touch_event(sapp_event_type type, NSSet<UITouch *>* touches, UIEvent* event) {
  4266. if (_sapp_events_enabled()) {
  4267. _sapp_init_event(type);
  4268. NSEnumerator* enumerator = event.allTouches.objectEnumerator;
  4269. UITouch* ios_touch;
  4270. while ((ios_touch = [enumerator nextObject])) {
  4271. if ((_sapp.event.num_touches + 1) < SAPP_MAX_TOUCHPOINTS) {
  4272. CGPoint ios_pos = [ios_touch locationInView:_sapp.ios.view];
  4273. sapp_touchpoint* cur_point = &_sapp.event.touches[_sapp.event.num_touches++];
  4274. cur_point->identifier = (uintptr_t) ios_touch;
  4275. cur_point->pos_x = ios_pos.x * _sapp.dpi_scale;
  4276. cur_point->pos_y = ios_pos.y * _sapp.dpi_scale;
  4277. cur_point->changed = [touches containsObject:ios_touch];
  4278. }
  4279. }
  4280. if (_sapp.event.num_touches > 0) {
  4281. _sapp_call_event(&_sapp.event);
  4282. }
  4283. }
  4284. }
  4285. _SOKOL_PRIVATE void _sapp_ios_update_dimensions(void) {
  4286. CGRect screen_rect = UIScreen.mainScreen.bounds;
  4287. _sapp.framebuffer_width = (int)(screen_rect.size.width * _sapp.dpi_scale);
  4288. _sapp.framebuffer_height = (int)(screen_rect.size.height * _sapp.dpi_scale);
  4289. _sapp.window_width = (int)screen_rect.size.width;
  4290. _sapp.window_height = (int)screen_rect.size.height;
  4291. int cur_fb_width, cur_fb_height;
  4292. #if defined(SOKOL_METAL)
  4293. const CGSize fb_size = _sapp.ios.view.drawableSize;
  4294. cur_fb_width = (int) fb_size.width;
  4295. cur_fb_height = (int) fb_size.height;
  4296. #else
  4297. cur_fb_width = (int) _sapp.ios.view.drawableWidth;
  4298. cur_fb_height = (int) _sapp.ios.view.drawableHeight;
  4299. #endif
  4300. const bool dim_changed = (_sapp.framebuffer_width != cur_fb_width) ||
  4301. (_sapp.framebuffer_height != cur_fb_height);
  4302. if (dim_changed) {
  4303. #if defined(SOKOL_METAL)
  4304. const CGSize drawable_size = { (CGFloat) _sapp.framebuffer_width, (CGFloat) _sapp.framebuffer_height };
  4305. _sapp.ios.view.drawableSize = drawable_size;
  4306. #else
  4307. // nothing to do here, GLKView correctly respects the view's contentScaleFactor
  4308. #endif
  4309. if (!_sapp.first_frame) {
  4310. _sapp_ios_app_event(SAPP_EVENTTYPE_RESIZED);
  4311. }
  4312. }
  4313. }
  4314. _SOKOL_PRIVATE void _sapp_ios_frame(void) {
  4315. _sapp_ios_update_dimensions();
  4316. _sapp_frame();
  4317. }
  4318. _SOKOL_PRIVATE void _sapp_ios_show_keyboard(bool shown) {
  4319. /* if not happened yet, create an invisible text field */
  4320. if (nil == _sapp.ios.textfield) {
  4321. _sapp.ios.textfield_dlg = [[_sapp_textfield_dlg alloc] init];
  4322. _sapp.ios.textfield = [[UITextField alloc] initWithFrame:CGRectMake(10, 10, 100, 50)];
  4323. _sapp.ios.textfield.keyboardType = UIKeyboardTypeDefault;
  4324. _sapp.ios.textfield.returnKeyType = UIReturnKeyDefault;
  4325. _sapp.ios.textfield.autocapitalizationType = UITextAutocapitalizationTypeNone;
  4326. _sapp.ios.textfield.autocorrectionType = UITextAutocorrectionTypeNo;
  4327. _sapp.ios.textfield.spellCheckingType = UITextSpellCheckingTypeNo;
  4328. _sapp.ios.textfield.hidden = YES;
  4329. _sapp.ios.textfield.text = @"x";
  4330. _sapp.ios.textfield.delegate = _sapp.ios.textfield_dlg;
  4331. [_sapp.ios.view_ctrl.view addSubview:_sapp.ios.textfield];
  4332. [[NSNotificationCenter defaultCenter] addObserver:_sapp.ios.textfield_dlg
  4333. selector:@selector(keyboardWasShown:)
  4334. name:UIKeyboardDidShowNotification object:nil];
  4335. [[NSNotificationCenter defaultCenter] addObserver:_sapp.ios.textfield_dlg
  4336. selector:@selector(keyboardWillBeHidden:)
  4337. name:UIKeyboardWillHideNotification object:nil];
  4338. [[NSNotificationCenter defaultCenter] addObserver:_sapp.ios.textfield_dlg
  4339. selector:@selector(keyboardDidChangeFrame:)
  4340. name:UIKeyboardDidChangeFrameNotification object:nil];
  4341. }
  4342. if (shown) {
  4343. /* setting the text field as first responder brings up the onscreen keyboard */
  4344. [_sapp.ios.textfield becomeFirstResponder];
  4345. }
  4346. else {
  4347. [_sapp.ios.textfield resignFirstResponder];
  4348. }
  4349. }
  4350. @implementation _sapp_app_delegate
  4351. - (BOOL)application:(UIApplication*)application didFinishLaunchingWithOptions:(NSDictionary*)launchOptions {
  4352. CGRect screen_rect = UIScreen.mainScreen.bounds;
  4353. _sapp.ios.window = [[UIWindow alloc] initWithFrame:screen_rect];
  4354. _sapp.window_width = screen_rect.size.width;
  4355. _sapp.window_height = screen_rect.size.height;
  4356. if (_sapp.desc.high_dpi) {
  4357. _sapp.dpi_scale = (float) UIScreen.mainScreen.nativeScale;
  4358. }
  4359. else {
  4360. _sapp.dpi_scale = 1.0f;
  4361. }
  4362. _sapp.framebuffer_width = _sapp.window_width * _sapp.dpi_scale;
  4363. _sapp.framebuffer_height = _sapp.window_height * _sapp.dpi_scale;
  4364. #if defined(SOKOL_METAL)
  4365. _sapp.ios.mtl_device = MTLCreateSystemDefaultDevice();
  4366. _sapp.ios.view = [[_sapp_ios_view alloc] init];
  4367. _sapp.ios.view.preferredFramesPerSecond = 60 / _sapp.swap_interval;
  4368. _sapp.ios.view.device = _sapp.ios.mtl_device;
  4369. _sapp.ios.view.colorPixelFormat = MTLPixelFormatBGRA8Unorm;
  4370. _sapp.ios.view.depthStencilPixelFormat = MTLPixelFormatDepth32Float_Stencil8;
  4371. _sapp.ios.view.sampleCount = (NSUInteger)_sapp.sample_count;
  4372. /* NOTE: iOS MTKView seems to ignore thew view's contentScaleFactor
  4373. and automatically renders at Retina resolution. We'll disable
  4374. autoResize and instead do the resizing in _sapp_ios_update_dimensions()
  4375. */
  4376. _sapp.ios.view.autoResizeDrawable = NO;
  4377. _sapp.ios.view.userInteractionEnabled = YES;
  4378. _sapp.ios.view.multipleTouchEnabled = YES;
  4379. _sapp.ios.view_ctrl = [[UIViewController alloc] init];
  4380. _sapp.ios.view_ctrl.modalPresentationStyle = UIModalPresentationFullScreen;
  4381. _sapp.ios.view_ctrl.view = _sapp.ios.view;
  4382. _sapp.ios.window.rootViewController = _sapp.ios.view_ctrl;
  4383. #else
  4384. if (_sapp.desc.gl_force_gles2) {
  4385. _sapp.ios.eagl_ctx = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];
  4386. _sapp.gles2_fallback = true;
  4387. }
  4388. else {
  4389. _sapp.ios.eagl_ctx = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES3];
  4390. if (_sapp.ios.eagl_ctx == nil) {
  4391. _sapp.ios.eagl_ctx = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];
  4392. _sapp.gles2_fallback = true;
  4393. }
  4394. }
  4395. _sapp.ios.view = [[_sapp_ios_view alloc] initWithFrame:screen_rect];
  4396. _sapp.ios.view.drawableColorFormat = GLKViewDrawableColorFormatRGBA8888;
  4397. _sapp.ios.view.drawableDepthFormat = GLKViewDrawableDepthFormat24;
  4398. _sapp.ios.view.drawableStencilFormat = GLKViewDrawableStencilFormatNone;
  4399. GLKViewDrawableMultisample msaa = _sapp.sample_count > 1 ? GLKViewDrawableMultisample4X : GLKViewDrawableMultisampleNone;
  4400. _sapp.ios.view.drawableMultisample = msaa;
  4401. _sapp.ios.view.context = _sapp.ios.eagl_ctx;
  4402. _sapp.ios.view.enableSetNeedsDisplay = NO;
  4403. _sapp.ios.view.userInteractionEnabled = YES;
  4404. _sapp.ios.view.multipleTouchEnabled = YES;
  4405. // on GLKView, contentScaleFactor appears to work just fine!
  4406. if (_sapp.desc.high_dpi) {
  4407. _sapp.ios.view.contentScaleFactor = 2.0;
  4408. }
  4409. else {
  4410. _sapp.ios.view.contentScaleFactor = 1.0;
  4411. }
  4412. _sapp.ios.view_ctrl = [[GLKViewController alloc] init];
  4413. _sapp.ios.view_ctrl.view = _sapp.ios.view;
  4414. _sapp.ios.view_ctrl.preferredFramesPerSecond = 60 / _sapp.swap_interval;
  4415. _sapp.ios.window.rootViewController = _sapp.ios.view_ctrl;
  4416. #endif
  4417. [_sapp.ios.window makeKeyAndVisible];
  4418. _sapp.valid = true;
  4419. return YES;
  4420. }
  4421. - (void)applicationWillResignActive:(UIApplication *)application {
  4422. if (!_sapp.ios.suspended) {
  4423. _sapp.ios.suspended = true;
  4424. _sapp_ios_app_event(SAPP_EVENTTYPE_SUSPENDED);
  4425. }
  4426. }
  4427. - (void)applicationDidBecomeActive:(UIApplication *)application {
  4428. if (_sapp.ios.suspended) {
  4429. _sapp.ios.suspended = false;
  4430. _sapp_ios_app_event(SAPP_EVENTTYPE_RESUMED);
  4431. }
  4432. }
  4433. /* NOTE: this method will rarely ever be called, iOS application
  4434. which are terminated by the user are usually killed via signal 9
  4435. by the operating system.
  4436. */
  4437. - (void)applicationWillTerminate:(UIApplication *)application {
  4438. _SOKOL_UNUSED(application);
  4439. _sapp_call_cleanup();
  4440. _sapp_ios_discard_state();
  4441. _sapp_discard_state();
  4442. }
  4443. @end
  4444. @implementation _sapp_textfield_dlg
  4445. - (void)keyboardWasShown:(NSNotification*)notif {
  4446. _sapp.onscreen_keyboard_shown = true;
  4447. /* query the keyboard's size, and modify the content view's size */
  4448. if (_sapp.desc.ios_keyboard_resizes_canvas) {
  4449. NSDictionary* info = notif.userInfo;
  4450. CGFloat kbd_h = [[info objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue].size.height;
  4451. CGRect view_frame = UIScreen.mainScreen.bounds;
  4452. view_frame.size.height -= kbd_h;
  4453. _sapp.ios.view.frame = view_frame;
  4454. }
  4455. }
  4456. - (void)keyboardWillBeHidden:(NSNotification*)notif {
  4457. _sapp.onscreen_keyboard_shown = false;
  4458. if (_sapp.desc.ios_keyboard_resizes_canvas) {
  4459. _sapp.ios.view.frame = UIScreen.mainScreen.bounds;
  4460. }
  4461. }
  4462. - (void)keyboardDidChangeFrame:(NSNotification*)notif {
  4463. /* this is for the case when the screen rotation changes while the keyboard is open */
  4464. if (_sapp.onscreen_keyboard_shown && _sapp.desc.ios_keyboard_resizes_canvas) {
  4465. NSDictionary* info = notif.userInfo;
  4466. CGFloat kbd_h = [[info objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue].size.height;
  4467. CGRect view_frame = UIScreen.mainScreen.bounds;
  4468. view_frame.size.height -= kbd_h;
  4469. _sapp.ios.view.frame = view_frame;
  4470. }
  4471. }
  4472. - (BOOL)textField:(UITextField*)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString*)string {
  4473. if (_sapp_events_enabled()) {
  4474. const NSUInteger len = string.length;
  4475. if (len > 0) {
  4476. for (NSUInteger i = 0; i < len; i++) {
  4477. unichar c = [string characterAtIndex:i];
  4478. if (c >= 32) {
  4479. /* ignore surrogates for now */
  4480. if ((c < 0xD800) || (c > 0xDFFF)) {
  4481. _sapp_init_event(SAPP_EVENTTYPE_CHAR);
  4482. _sapp.event.char_code = c;
  4483. _sapp_call_event(&_sapp.event);
  4484. }
  4485. }
  4486. if (c <= 32) {
  4487. sapp_keycode k = SAPP_KEYCODE_INVALID;
  4488. switch (c) {
  4489. case 10: k = SAPP_KEYCODE_ENTER; break;
  4490. case 32: k = SAPP_KEYCODE_SPACE; break;
  4491. default: break;
  4492. }
  4493. if (k != SAPP_KEYCODE_INVALID) {
  4494. _sapp_init_event(SAPP_EVENTTYPE_KEY_DOWN);
  4495. _sapp.event.key_code = k;
  4496. _sapp_call_event(&_sapp.event);
  4497. _sapp_init_event(SAPP_EVENTTYPE_KEY_UP);
  4498. _sapp.event.key_code = k;
  4499. _sapp_call_event(&_sapp.event);
  4500. }
  4501. }
  4502. }
  4503. }
  4504. else {
  4505. /* this was a backspace */
  4506. _sapp_init_event(SAPP_EVENTTYPE_KEY_DOWN);
  4507. _sapp.event.key_code = SAPP_KEYCODE_BACKSPACE;
  4508. _sapp_call_event(&_sapp.event);
  4509. _sapp_init_event(SAPP_EVENTTYPE_KEY_UP);
  4510. _sapp.event.key_code = SAPP_KEYCODE_BACKSPACE;
  4511. _sapp_call_event(&_sapp.event);
  4512. }
  4513. }
  4514. return NO;
  4515. }
  4516. @end
  4517. @implementation _sapp_ios_view
  4518. - (void)drawRect:(CGRect)rect {
  4519. _SOKOL_UNUSED(rect);
  4520. @autoreleasepool
  4521. _sapp_ios_frame();
  4522. }
  4523. }
  4524. - (BOOL)isOpaque {
  4525. return YES;
  4526. }
  4527. - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent*)event {
  4528. _sapp_ios_touch_event(SAPP_EVENTTYPE_TOUCHES_BEGAN, touches, event);
  4529. }
  4530. - (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(UIEvent*)event {
  4531. _sapp_ios_touch_event(SAPP_EVENTTYPE_TOUCHES_MOVED, touches, event);
  4532. }
  4533. - (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent*)event {
  4534. _sapp_ios_touch_event(SAPP_EVENTTYPE_TOUCHES_ENDED, touches, event);
  4535. }
  4536. - (void)touchesCancelled:(NSSet<UITouch *> *)touches withEvent:(UIEvent*)event {
  4537. _sapp_ios_touch_event(SAPP_EVENTTYPE_TOUCHES_CANCELLED, touches, event);
  4538. }
  4539. @end
  4540. #endif /* TARGET_OS_IPHONE */
  4541. #endif /* _SAPP_APPLE */
  4542. /*== EMSCRIPTEN ==============================================================*/
  4543. #if defined(_SAPP_EMSCRIPTEN)
  4544. #ifdef __cplusplus
  4545. extern "C" {
  4546. #endif
  4547. typedef void (*_sapp_html5_fetch_callback) (const sapp_html5_fetch_response*);
  4548. /* this function is called from a JS event handler when the user hides
  4549. the onscreen keyboard pressing the 'dismiss keyboard key'
  4550. */
  4551. EMSCRIPTEN_KEEPALIVE void _sapp_emsc_notify_keyboard_hidden(void) {
  4552. _sapp.onscreen_keyboard_shown = false;
  4553. }
  4554. EMSCRIPTEN_KEEPALIVE void _sapp_emsc_onpaste(const char* str) {
  4555. if (_sapp.clipboard.enabled) {
  4556. _sapp_strcpy(str, _sapp.clipboard.buffer, _sapp.clipboard.buf_size);
  4557. if (_sapp_events_enabled()) {
  4558. _sapp_init_event(SAPP_EVENTTYPE_CLIPBOARD_PASTED);
  4559. _sapp_call_event(&_sapp.event);
  4560. }
  4561. }
  4562. }
  4563. /* https://developer.mozilla.org/en-US/docs/Web/API/WindowEventHandlers/onbeforeunload */
  4564. EMSCRIPTEN_KEEPALIVE int _sapp_html5_get_ask_leave_site(void) {
  4565. return _sapp.html5_ask_leave_site ? 1 : 0;
  4566. }
  4567. EMSCRIPTEN_KEEPALIVE void _sapp_emsc_begin_drop(int num) {
  4568. if (!_sapp.drop.enabled) {
  4569. return;
  4570. }
  4571. if (num < 0) {
  4572. num = 0;
  4573. }
  4574. if (num > _sapp.drop.max_files) {
  4575. num = _sapp.drop.max_files;
  4576. }
  4577. _sapp.drop.num_files = num;
  4578. _sapp_clear_drop_buffer();
  4579. }
  4580. EMSCRIPTEN_KEEPALIVE void _sapp_emsc_drop(int i, const char* name) {
  4581. /* NOTE: name is only the filename part, not a path */
  4582. if (!_sapp.drop.enabled) {
  4583. return;
  4584. }
  4585. if (0 == name) {
  4586. return;
  4587. }
  4588. SOKOL_ASSERT(_sapp.drop.num_files <= _sapp.drop.max_files);
  4589. if ((i < 0) || (i >= _sapp.drop.num_files)) {
  4590. return;
  4591. }
  4592. if (!_sapp_strcpy(name, _sapp_dropped_file_path_ptr(i), _sapp.drop.max_path_length)) {
  4593. SOKOL_LOG("sokol_app.h: dropped file path too long!\n");
  4594. _sapp.drop.num_files = 0;
  4595. }
  4596. }
  4597. EMSCRIPTEN_KEEPALIVE void _sapp_emsc_end_drop(int x, int y) {
  4598. if (!_sapp.drop.enabled) {
  4599. return;
  4600. }
  4601. if (0 == _sapp.drop.num_files) {
  4602. /* there was an error copying the filenames */
  4603. _sapp_clear_drop_buffer();
  4604. return;
  4605. }
  4606. if (_sapp_events_enabled()) {
  4607. _sapp.mouse.x = (float)x * _sapp.dpi_scale;
  4608. _sapp.mouse.y = (float)y * _sapp.dpi_scale;
  4609. _sapp.mouse.dx = 0.0f;
  4610. _sapp.mouse.dy = 0.0f;
  4611. _sapp_init_event(SAPP_EVENTTYPE_FILES_DROPPED);
  4612. _sapp_call_event(&_sapp.event);
  4613. }
  4614. }
  4615. EMSCRIPTEN_KEEPALIVE void _sapp_emsc_invoke_fetch_cb(int index, int success, int error_code, _sapp_html5_fetch_callback callback, uint32_t fetched_size, void* buf_ptr, uint32_t buf_size, void* user_data) {
  4616. sapp_html5_fetch_response response;
  4617. memset(&response, 0, sizeof(response));
  4618. response.succeeded = (0 != success);
  4619. response.error_code = (sapp_html5_fetch_error) error_code;
  4620. response.file_index = index;
  4621. response.fetched_size = fetched_size;
  4622. response.buffer_ptr = buf_ptr;
  4623. response.buffer_size = buf_size;
  4624. response.user_data = user_data;
  4625. callback(&response);
  4626. }
  4627. #ifdef __cplusplus
  4628. } /* extern "C" */
  4629. #endif
  4630. /* Javascript helper functions for mobile virtual keyboard input */
  4631. EM_JS(void, sapp_js_create_textfield, (void), {
  4632. var _sapp_inp = document.createElement("input");
  4633. _sapp_inp.type = "text";
  4634. _sapp_inp.id = "_sokol_app_input_element";
  4635. _sapp_inp.autocapitalize = "none";
  4636. _sapp_inp.addEventListener("focusout", function(_sapp_event) {
  4637. __sapp_emsc_notify_keyboard_hidden()
  4638. });
  4639. document.body.append(_sapp_inp);
  4640. });
  4641. EM_JS(void, sapp_js_focus_textfield, (void), {
  4642. document.getElementById("_sokol_app_input_element").focus();
  4643. });
  4644. EM_JS(void, sapp_js_unfocus_textfield, (void), {
  4645. document.getElementById("_sokol_app_input_element").blur();
  4646. });
  4647. EM_JS(void, sapp_js_add_beforeunload_listener, (void), {
  4648. Module.sokol_beforeunload = function(event) {
  4649. if (__sapp_html5_get_ask_leave_site() != 0) {
  4650. event.preventDefault();
  4651. event.returnValue = ' ';
  4652. }
  4653. };
  4654. window.addEventListener('beforeunload', Module.sokol_beforeunload);
  4655. });
  4656. EM_JS(void, sapp_js_remove_beforeunload_listener, (void), {
  4657. window.removeEventListener('beforeunload', Module.sokol_beforeunload);
  4658. });
  4659. EM_JS(void, sapp_js_add_clipboard_listener, (void), {
  4660. Module.sokol_paste = function(event) {
  4661. var pasted_str = event.clipboardData.getData('text');
  4662. ccall('_sapp_emsc_onpaste', 'void', ['string'], [pasted_str]);
  4663. };
  4664. window.addEventListener('paste', Module.sokol_paste);
  4665. });
  4666. EM_JS(void, sapp_js_remove_clipboard_listener, (void), {
  4667. window.removeEventListener('paste', Module.sokol_paste);
  4668. });
  4669. EM_JS(void, sapp_js_write_clipboard, (const char* c_str), {
  4670. var str = UTF8ToString(c_str);
  4671. var ta = document.createElement('textarea');
  4672. ta.setAttribute('autocomplete', 'off');
  4673. ta.setAttribute('autocorrect', 'off');
  4674. ta.setAttribute('autocapitalize', 'off');
  4675. ta.setAttribute('spellcheck', 'false');
  4676. ta.style.left = -100 + 'px';
  4677. ta.style.top = -100 + 'px';
  4678. ta.style.height = 1;
  4679. ta.style.width = 1;
  4680. ta.value = str;
  4681. document.body.appendChild(ta);
  4682. ta.select();
  4683. document.execCommand('copy');
  4684. document.body.removeChild(ta);
  4685. });
  4686. _SOKOL_PRIVATE void _sapp_emsc_set_clipboard_string(const char* str) {
  4687. sapp_js_write_clipboard(str);
  4688. }
  4689. EM_JS(void, sapp_js_add_dragndrop_listeners, (const char* canvas_name_cstr), {
  4690. Module.sokol_drop_files = [];
  4691. var canvas_name = UTF8ToString(canvas_name_cstr);
  4692. var canvas = document.getElementById(canvas_name);
  4693. Module.sokol_dragenter = function(event) {
  4694. event.stopPropagation();
  4695. event.preventDefault();
  4696. };
  4697. Module.sokol_dragleave = function(event) {
  4698. event.stopPropagation();
  4699. event.preventDefault();
  4700. };
  4701. Module.sokol_dragover = function(event) {
  4702. event.stopPropagation();
  4703. event.preventDefault();
  4704. };
  4705. Module.sokol_drop = function(event) {
  4706. event.stopPropagation();
  4707. event.preventDefault();
  4708. var files = event.dataTransfer.files;
  4709. Module.sokol_dropped_files = files;
  4710. __sapp_emsc_begin_drop(files.length);
  4711. var i;
  4712. for (i = 0; i < files.length; i++) {
  4713. ccall('_sapp_emsc_drop', 'void', ['number', 'string'], [i, files[i].name]);
  4714. }
  4715. // FIXME? see computation of targetX/targetY in emscripten via getClientBoundingRect
  4716. __sapp_emsc_end_drop(event.clientX, event.clientY);
  4717. };
  4718. canvas.addEventListener('dragenter', Module.sokol_dragenter, false);
  4719. canvas.addEventListener('dragleave', Module.sokol_dragleave, false);
  4720. canvas.addEventListener('dragover', Module.sokol_dragover, false);
  4721. canvas.addEventListener('drop', Module.sokol_drop, false);
  4722. });
  4723. EM_JS(uint32_t, sapp_js_dropped_file_size, (int index), {
  4724. if ((index < 0) || (index >= Module.sokol_dropped_files.length)) {
  4725. return 0;
  4726. }
  4727. else {
  4728. return Module.sokol_dropped_files[index].size;
  4729. }
  4730. });
  4731. EM_JS(void, sapp_js_fetch_dropped_file, (int index, _sapp_html5_fetch_callback callback, void* buf_ptr, uint32_t buf_size, void* user_data), {
  4732. var reader = new FileReader();
  4733. reader.onload = function(loadEvent) {
  4734. var content = loadEvent.target.result;
  4735. if (content.byteLength > buf_size) {
  4736. // SAPP_HTML5_FETCH_ERROR_BUFFER_TOO_SMALL
  4737. __sapp_emsc_invoke_fetch_cb(index, 0, 1, callback, 0, buf_ptr, buf_size, user_data);
  4738. }
  4739. else {
  4740. HEAPU8.set(new Uint8Array(content), buf_ptr);
  4741. __sapp_emsc_invoke_fetch_cb(index, 1, 0, callback, content.byteLength, buf_ptr, buf_size, user_data);
  4742. }
  4743. };
  4744. reader.onerror = function() {
  4745. // SAPP_HTML5_FETCH_ERROR_OTHER
  4746. __sapp_emsc_invoke_fetch_cb(index, 0, 2, callback, 0, buf_ptr, buf_size, user_data);
  4747. };
  4748. reader.readAsArrayBuffer(Module.sokol_dropped_files[index]);
  4749. });
  4750. EM_JS(void, sapp_js_remove_dragndrop_listeners, (const char* canvas_name_cstr), {
  4751. var canvas_name = UTF8ToString(canvas_name_cstr);
  4752. var canvas = document.getElementById(canvas_name);
  4753. canvas.removeEventListener('dragenter', Module.sokol_dragenter);
  4754. canvas.removeEventListener('dragleave', Module.sokol_dragleave);
  4755. canvas.removeEventListener('dragover', Module.sokol_dragover);
  4756. canvas.removeEventListener('drop', Module.sokol_drop);
  4757. });
  4758. /* called from the emscripten event handler to update the keyboard visibility
  4759. state, this must happen from an JS input event handler, otherwise
  4760. the request will be ignored by the browser
  4761. */
  4762. _SOKOL_PRIVATE void _sapp_emsc_update_keyboard_state(void) {
  4763. if (_sapp.emsc.wants_show_keyboard) {
  4764. /* create input text field on demand */
  4765. if (!_sapp.emsc.textfield_created) {
  4766. _sapp.emsc.textfield_created = true;
  4767. sapp_js_create_textfield();
  4768. }
  4769. /* focus the text input field, this will bring up the keyboard */
  4770. _sapp.onscreen_keyboard_shown = true;
  4771. _sapp.emsc.wants_show_keyboard = false;
  4772. sapp_js_focus_textfield();
  4773. }
  4774. if (_sapp.emsc.wants_hide_keyboard) {
  4775. /* unfocus the text input field */
  4776. if (_sapp.emsc.textfield_created) {
  4777. _sapp.onscreen_keyboard_shown = false;
  4778. _sapp.emsc.wants_hide_keyboard = false;
  4779. sapp_js_unfocus_textfield();
  4780. }
  4781. }
  4782. }
  4783. /* actually showing the onscreen keyboard must be initiated from a JS
  4784. input event handler, so we'll just keep track of the desired
  4785. state, and the actual state change will happen with the next input event
  4786. */
  4787. _SOKOL_PRIVATE void _sapp_emsc_show_keyboard(bool show) {
  4788. if (show) {
  4789. _sapp.emsc.wants_show_keyboard = true;
  4790. }
  4791. else {
  4792. _sapp.emsc.wants_hide_keyboard = true;
  4793. }
  4794. }
  4795. EM_JS(void, sapp_js_pointer_init, (const char* c_str_target), {
  4796. // lookup and store canvas object by name
  4797. var target_str = UTF8ToString(c_str_target);
  4798. Module.sapp_emsc_target = document.getElementById(target_str);
  4799. if (!Module.sapp_emsc_target) {
  4800. console.log("sokol_app.h: invalid target:" + target_str);
  4801. }
  4802. if (!Module.sapp_emsc_target.requestPointerLock) {
  4803. console.log("sokol_app.h: target doesn't support requestPointerLock:" + target_str);
  4804. }
  4805. });
  4806. _SOKOL_PRIVATE EM_BOOL _sapp_emsc_pointerlockchange_cb(int emsc_type, const EmscriptenPointerlockChangeEvent* emsc_event, void* user_data) {
  4807. _SOKOL_UNUSED(emsc_type);
  4808. _SOKOL_UNUSED(user_data);
  4809. _sapp.mouse.locked = emsc_event->isActive;
  4810. return EM_TRUE;
  4811. }
  4812. _SOKOL_PRIVATE EM_BOOL _sapp_emsc_pointerlockerror_cb(int emsc_type, const void* reserved, void* user_data) {
  4813. _SOKOL_UNUSED(emsc_type);
  4814. _SOKOL_UNUSED(reserved);
  4815. _SOKOL_UNUSED(user_data);
  4816. _sapp.mouse.locked = false;
  4817. _sapp.emsc.mouse_lock_requested = false;
  4818. return true;
  4819. }
  4820. EM_JS(void, sapp_js_request_pointerlock, (void), {
  4821. if (Module.sapp_emsc_target) {
  4822. if (Module.sapp_emsc_target.requestPointerLock) {
  4823. Module.sapp_emsc_target.requestPointerLock();
  4824. }
  4825. }
  4826. });
  4827. EM_JS(void, sapp_js_exit_pointerlock, (void), {
  4828. if (document.exitPointerLock) {
  4829. document.exitPointerLock();
  4830. }
  4831. });
  4832. _SOKOL_PRIVATE void _sapp_emsc_lock_mouse(bool lock) {
  4833. if (lock) {
  4834. /* request mouse-lock during event handler invocation (see _sapp_emsc_update_mouse_lock_state) */
  4835. _sapp.emsc.mouse_lock_requested = true;
  4836. }
  4837. else {
  4838. /* NOTE: the _sapp.mouse_locked state will be set in the pointerlockchange callback */
  4839. _sapp.emsc.mouse_lock_requested = false;
  4840. sapp_js_exit_pointerlock();
  4841. }
  4842. }
  4843. /* called from inside event handlers to check if mouse lock had been requested,
  4844. and if yes, actually enter mouse lock.
  4845. */
  4846. _SOKOL_PRIVATE void _sapp_emsc_update_mouse_lock_state(void) {
  4847. if (_sapp.emsc.mouse_lock_requested) {
  4848. _sapp.emsc.mouse_lock_requested = false;
  4849. sapp_js_request_pointerlock();
  4850. }
  4851. }
  4852. /* JS helper functions to update browser tab favicon */
  4853. EM_JS(void, sapp_js_clear_favicon, (void), {
  4854. var link = document.getElementById('sokol-app-favicon');
  4855. if (link) {
  4856. document.head.removeChild(link);
  4857. }
  4858. });
  4859. EM_JS(void, sapp_js_set_favicon, (int w, int h, const uint8_t* pixels), {
  4860. var canvas = document.createElement('canvas');
  4861. canvas.width = w;
  4862. canvas.height = h;
  4863. var ctx = canvas.getContext('2d');
  4864. var img_data = ctx.createImageData(w, h);
  4865. img_data.data.set(HEAPU8.subarray(pixels, pixels + w*h*4));
  4866. ctx.putImageData(img_data, 0, 0);
  4867. var new_link = document.createElement('link');
  4868. new_link.id = 'sokol-app-favicon';
  4869. new_link.rel = 'shortcut icon';
  4870. new_link.href = canvas.toDataURL();
  4871. document.head.appendChild(new_link);
  4872. });
  4873. _SOKOL_PRIVATE void _sapp_emsc_set_icon(const sapp_icon_desc* icon_desc, int num_images) {
  4874. SOKOL_ASSERT((num_images > 0) && (num_images <= SAPP_MAX_ICONIMAGES));
  4875. sapp_js_clear_favicon();
  4876. // find the best matching image candidate for 16x16 pixels
  4877. int img_index = _sapp_image_bestmatch(icon_desc->images, num_images, 16, 16);
  4878. const sapp_image_desc* img_desc = &icon_desc->images[img_index];
  4879. sapp_js_set_favicon(img_desc->width, img_desc->height, (const uint8_t*) img_desc->pixels.ptr);
  4880. }
  4881. #if defined(SOKOL_WGPU)
  4882. _SOKOL_PRIVATE void _sapp_emsc_wgpu_surfaces_create(void);
  4883. _SOKOL_PRIVATE void _sapp_emsc_wgpu_surfaces_discard(void);
  4884. #endif
  4885. _SOKOL_PRIVATE uint32_t _sapp_emsc_mouse_button_mods(uint16_t buttons) {
  4886. uint32_t m = 0;
  4887. if (0 != (buttons & (1<<0))) { m |= SAPP_MODIFIER_LMB; }
  4888. if (0 != (buttons & (1<<1))) { m |= SAPP_MODIFIER_RMB; } // not a bug
  4889. if (0 != (buttons & (1<<2))) { m |= SAPP_MODIFIER_MMB; } // not a bug
  4890. return m;
  4891. }
  4892. _SOKOL_PRIVATE uint32_t _sapp_emsc_mouse_event_mods(const EmscriptenMouseEvent* ev) {
  4893. uint32_t m = 0;
  4894. if (ev->ctrlKey) { m |= SAPP_MODIFIER_CTRL; }
  4895. if (ev->shiftKey) { m |= SAPP_MODIFIER_SHIFT; }
  4896. if (ev->altKey) { m |= SAPP_MODIFIER_ALT; }
  4897. if (ev->metaKey) { m |= SAPP_MODIFIER_SUPER; }
  4898. m |= _sapp_emsc_mouse_button_mods(_sapp.emsc.mouse_buttons);
  4899. return m;
  4900. }
  4901. _SOKOL_PRIVATE uint32_t _sapp_emsc_key_event_mods(const EmscriptenKeyboardEvent* ev) {
  4902. uint32_t m = 0;
  4903. if (ev->ctrlKey) { m |= SAPP_MODIFIER_CTRL; }
  4904. if (ev->shiftKey) { m |= SAPP_MODIFIER_SHIFT; }
  4905. if (ev->altKey) { m |= SAPP_MODIFIER_ALT; }
  4906. if (ev->metaKey) { m |= SAPP_MODIFIER_SUPER; }
  4907. m |= _sapp_emsc_mouse_button_mods(_sapp.emsc.mouse_buttons);
  4908. return m;
  4909. }
  4910. _SOKOL_PRIVATE uint32_t _sapp_emsc_touch_event_mods(const EmscriptenTouchEvent* ev) {
  4911. uint32_t m = 0;
  4912. if (ev->ctrlKey) { m |= SAPP_MODIFIER_CTRL; }
  4913. if (ev->shiftKey) { m |= SAPP_MODIFIER_SHIFT; }
  4914. if (ev->altKey) { m |= SAPP_MODIFIER_ALT; }
  4915. if (ev->metaKey) { m |= SAPP_MODIFIER_SUPER; }
  4916. m |= _sapp_emsc_mouse_button_mods(_sapp.emsc.mouse_buttons);
  4917. return m;
  4918. }
  4919. _SOKOL_PRIVATE EM_BOOL _sapp_emsc_size_changed(int event_type, const EmscriptenUiEvent* ui_event, void* user_data) {
  4920. _SOKOL_UNUSED(event_type);
  4921. _SOKOL_UNUSED(user_data);
  4922. double w, h;
  4923. emscripten_get_element_css_size(_sapp.html5_canvas_selector, &w, &h);
  4924. /* The above method might report zero when toggling HTML5 fullscreen,
  4925. in that case use the window's inner width reported by the
  4926. emscripten event. This works ok when toggling *into* fullscreen
  4927. but doesn't properly restore the previous canvas size when switching
  4928. back from fullscreen.
  4929. In general, due to the HTML5's fullscreen API's flaky nature it is
  4930. recommended to use 'soft fullscreen' (stretching the WebGL canvas
  4931. over the browser windows client rect) with a CSS definition like this:
  4932. position: absolute;
  4933. top: 0px;
  4934. left: 0px;
  4935. margin: 0px;
  4936. border: 0;
  4937. width: 100%;
  4938. height: 100%;
  4939. overflow: hidden;
  4940. display: block;
  4941. */
  4942. if (w < 1.0) {
  4943. w = ui_event->windowInnerWidth;
  4944. }
  4945. else {
  4946. _sapp.window_width = (int) w;
  4947. }
  4948. if (h < 1.0) {
  4949. h = ui_event->windowInnerHeight;
  4950. }
  4951. else {
  4952. _sapp.window_height = (int) h;
  4953. }
  4954. if (_sapp.desc.high_dpi) {
  4955. _sapp.dpi_scale = emscripten_get_device_pixel_ratio();
  4956. }
  4957. _sapp.framebuffer_width = (int) (w * _sapp.dpi_scale);
  4958. _sapp.framebuffer_height = (int) (h * _sapp.dpi_scale);
  4959. SOKOL_ASSERT((_sapp.framebuffer_width > 0) && (_sapp.framebuffer_height > 0));
  4960. emscripten_set_canvas_element_size(_sapp.html5_canvas_selector, _sapp.framebuffer_width, _sapp.framebuffer_height);
  4961. #if defined(SOKOL_WGPU)
  4962. /* on WebGPU: recreate size-dependent rendering surfaces */
  4963. _sapp_emsc_wgpu_surfaces_discard();
  4964. _sapp_emsc_wgpu_surfaces_create();
  4965. #endif
  4966. if (_sapp_events_enabled()) {
  4967. _sapp_init_event(SAPP_EVENTTYPE_RESIZED);
  4968. _sapp_call_event(&_sapp.event);
  4969. }
  4970. return true;
  4971. }
  4972. _SOKOL_PRIVATE EM_BOOL _sapp_emsc_mouse_cb(int emsc_type, const EmscriptenMouseEvent* emsc_event, void* user_data) {
  4973. _SOKOL_UNUSED(user_data);
  4974. _sapp.emsc.mouse_buttons = emsc_event->buttons;
  4975. if (_sapp.mouse.locked) {
  4976. _sapp.mouse.dx = (float) emsc_event->movementX;
  4977. _sapp.mouse.dy = (float) emsc_event->movementY;
  4978. }
  4979. else {
  4980. float new_x = emsc_event->targetX * _sapp.dpi_scale;
  4981. float new_y = emsc_event->targetY * _sapp.dpi_scale;
  4982. if (_sapp.mouse.pos_valid) {
  4983. _sapp.mouse.dx = new_x - _sapp.mouse.x;
  4984. _sapp.mouse.dy = new_y - _sapp.mouse.y;
  4985. }
  4986. _sapp.mouse.x = new_x;
  4987. _sapp.mouse.y = new_y;
  4988. _sapp.mouse.pos_valid = true;
  4989. }
  4990. if (_sapp_events_enabled() && (emsc_event->button >= 0) && (emsc_event->button < SAPP_MAX_MOUSEBUTTONS)) {
  4991. sapp_event_type type;
  4992. bool is_button_event = false;
  4993. switch (emsc_type) {
  4994. case EMSCRIPTEN_EVENT_MOUSEDOWN:
  4995. type = SAPP_EVENTTYPE_MOUSE_DOWN;
  4996. is_button_event = true;
  4997. break;
  4998. case EMSCRIPTEN_EVENT_MOUSEUP:
  4999. type = SAPP_EVENTTYPE_MOUSE_UP;
  5000. is_button_event = true;
  5001. break;
  5002. case EMSCRIPTEN_EVENT_MOUSEMOVE:
  5003. type = SAPP_EVENTTYPE_MOUSE_MOVE;
  5004. break;
  5005. case EMSCRIPTEN_EVENT_MOUSEENTER:
  5006. type = SAPP_EVENTTYPE_MOUSE_ENTER;
  5007. break;
  5008. case EMSCRIPTEN_EVENT_MOUSELEAVE:
  5009. type = SAPP_EVENTTYPE_MOUSE_LEAVE;
  5010. break;
  5011. default:
  5012. type = SAPP_EVENTTYPE_INVALID;
  5013. break;
  5014. }
  5015. if (type != SAPP_EVENTTYPE_INVALID) {
  5016. _sapp_init_event(type);
  5017. _sapp.event.modifiers = _sapp_emsc_mouse_event_mods(emsc_event);
  5018. if (is_button_event) {
  5019. switch (emsc_event->button) {
  5020. case 0: _sapp.event.mouse_button = SAPP_MOUSEBUTTON_LEFT; break;
  5021. case 1: _sapp.event.mouse_button = SAPP_MOUSEBUTTON_MIDDLE; break;
  5022. case 2: _sapp.event.mouse_button = SAPP_MOUSEBUTTON_RIGHT; break;
  5023. default: _sapp.event.mouse_button = (sapp_mousebutton)emsc_event->button; break;
  5024. }
  5025. }
  5026. else {
  5027. _sapp.event.mouse_button = SAPP_MOUSEBUTTON_INVALID;
  5028. }
  5029. _sapp_call_event(&_sapp.event);
  5030. }
  5031. /* mouse lock can only be activated in mouse button events (not in move, enter or leave) */
  5032. if (is_button_event) {
  5033. _sapp_emsc_update_mouse_lock_state();
  5034. }
  5035. }
  5036. _sapp_emsc_update_keyboard_state();
  5037. return true;
  5038. }
  5039. _SOKOL_PRIVATE EM_BOOL _sapp_emsc_wheel_cb(int emsc_type, const EmscriptenWheelEvent* emsc_event, void* user_data) {
  5040. _SOKOL_UNUSED(emsc_type);
  5041. _SOKOL_UNUSED(user_data);
  5042. _sapp.emsc.mouse_buttons = emsc_event->mouse.buttons;
  5043. if (_sapp_events_enabled()) {
  5044. _sapp_init_event(SAPP_EVENTTYPE_MOUSE_SCROLL);
  5045. _sapp.event.modifiers = _sapp_emsc_mouse_event_mods(&emsc_event->mouse);
  5046. /* see https://github.com/floooh/sokol/issues/339 */
  5047. float scale;
  5048. switch (emsc_event->deltaMode) {
  5049. case DOM_DELTA_PIXEL: scale = -0.04f; break;
  5050. case DOM_DELTA_LINE: scale = -1.33f; break;
  5051. case DOM_DELTA_PAGE: scale = -10.0f; break; // FIXME: this is a guess
  5052. default: scale = -0.1f; break; // shouldn't happen
  5053. }
  5054. _sapp.event.scroll_x = scale * (float)emsc_event->deltaX;
  5055. _sapp.event.scroll_y = scale * (float)emsc_event->deltaY;
  5056. _sapp_call_event(&_sapp.event);
  5057. }
  5058. _sapp_emsc_update_keyboard_state();
  5059. _sapp_emsc_update_mouse_lock_state();
  5060. return true;
  5061. }
  5062. _SOKOL_PRIVATE EM_BOOL _sapp_emsc_key_cb(int emsc_type, const EmscriptenKeyboardEvent* emsc_event, void* user_data) {
  5063. _SOKOL_UNUSED(user_data);
  5064. bool retval = true;
  5065. if (_sapp_events_enabled()) {
  5066. sapp_event_type type;
  5067. switch (emsc_type) {
  5068. case EMSCRIPTEN_EVENT_KEYDOWN:
  5069. type = SAPP_EVENTTYPE_KEY_DOWN;
  5070. break;
  5071. case EMSCRIPTEN_EVENT_KEYUP:
  5072. type = SAPP_EVENTTYPE_KEY_UP;
  5073. break;
  5074. case EMSCRIPTEN_EVENT_KEYPRESS:
  5075. type = SAPP_EVENTTYPE_CHAR;
  5076. break;
  5077. default:
  5078. type = SAPP_EVENTTYPE_INVALID;
  5079. break;
  5080. }
  5081. if (type != SAPP_EVENTTYPE_INVALID) {
  5082. bool send_keyup_followup = false;
  5083. _sapp_init_event(type);
  5084. _sapp.event.key_repeat = emsc_event->repeat;
  5085. _sapp.event.modifiers = _sapp_emsc_key_event_mods(emsc_event);
  5086. if (type == SAPP_EVENTTYPE_CHAR) {
  5087. _sapp.event.char_code = emsc_event->charCode;
  5088. /* workaround to make Cmd+V work on Safari */
  5089. if ((emsc_event->metaKey) && (emsc_event->charCode == 118)) {
  5090. retval = false;
  5091. }
  5092. }
  5093. else {
  5094. _sapp.event.key_code = _sapp_translate_key((int)emsc_event->keyCode);
  5095. /* Special hack for macOS: if the Super key is pressed, macOS doesn't
  5096. send keyUp events. As a workaround, to prevent keys from
  5097. "sticking", we'll send a keyup event following a keydown
  5098. when the SUPER key is pressed
  5099. */
  5100. if ((type == SAPP_EVENTTYPE_KEY_DOWN) &&
  5101. (_sapp.event.key_code != SAPP_KEYCODE_LEFT_SUPER) &&
  5102. (_sapp.event.key_code != SAPP_KEYCODE_RIGHT_SUPER) &&
  5103. (_sapp.event.modifiers & SAPP_MODIFIER_SUPER))
  5104. {
  5105. send_keyup_followup = true;
  5106. }
  5107. /* only forward a certain key ranges to the browser */
  5108. switch (_sapp.event.key_code) {
  5109. case SAPP_KEYCODE_WORLD_1:
  5110. case SAPP_KEYCODE_WORLD_2:
  5111. case SAPP_KEYCODE_ESCAPE:
  5112. case SAPP_KEYCODE_ENTER:
  5113. case SAPP_KEYCODE_TAB:
  5114. case SAPP_KEYCODE_BACKSPACE:
  5115. case SAPP_KEYCODE_INSERT:
  5116. case SAPP_KEYCODE_DELETE:
  5117. case SAPP_KEYCODE_RIGHT:
  5118. case SAPP_KEYCODE_LEFT:
  5119. case SAPP_KEYCODE_DOWN:
  5120. case SAPP_KEYCODE_UP:
  5121. case SAPP_KEYCODE_PAGE_UP:
  5122. case SAPP_KEYCODE_PAGE_DOWN:
  5123. case SAPP_KEYCODE_HOME:
  5124. case SAPP_KEYCODE_END:
  5125. case SAPP_KEYCODE_CAPS_LOCK:
  5126. case SAPP_KEYCODE_SCROLL_LOCK:
  5127. case SAPP_KEYCODE_NUM_LOCK:
  5128. case SAPP_KEYCODE_PRINT_SCREEN:
  5129. case SAPP_KEYCODE_PAUSE:
  5130. case SAPP_KEYCODE_F1:
  5131. case SAPP_KEYCODE_F2:
  5132. case SAPP_KEYCODE_F3:
  5133. case SAPP_KEYCODE_F4:
  5134. case SAPP_KEYCODE_F5:
  5135. case SAPP_KEYCODE_F6:
  5136. case SAPP_KEYCODE_F7:
  5137. case SAPP_KEYCODE_F8:
  5138. case SAPP_KEYCODE_F9:
  5139. case SAPP_KEYCODE_F10:
  5140. case SAPP_KEYCODE_F11:
  5141. case SAPP_KEYCODE_F12:
  5142. case SAPP_KEYCODE_F13:
  5143. case SAPP_KEYCODE_F14:
  5144. case SAPP_KEYCODE_F15:
  5145. case SAPP_KEYCODE_F16:
  5146. case SAPP_KEYCODE_F17:
  5147. case SAPP_KEYCODE_F18:
  5148. case SAPP_KEYCODE_F19:
  5149. case SAPP_KEYCODE_F20:
  5150. case SAPP_KEYCODE_F21:
  5151. case SAPP_KEYCODE_F22:
  5152. case SAPP_KEYCODE_F23:
  5153. case SAPP_KEYCODE_F24:
  5154. case SAPP_KEYCODE_F25:
  5155. case SAPP_KEYCODE_LEFT_SHIFT:
  5156. case SAPP_KEYCODE_LEFT_CONTROL:
  5157. case SAPP_KEYCODE_LEFT_ALT:
  5158. case SAPP_KEYCODE_LEFT_SUPER:
  5159. case SAPP_KEYCODE_RIGHT_SHIFT:
  5160. case SAPP_KEYCODE_RIGHT_CONTROL:
  5161. case SAPP_KEYCODE_RIGHT_ALT:
  5162. case SAPP_KEYCODE_RIGHT_SUPER:
  5163. case SAPP_KEYCODE_MENU:
  5164. /* consume the event */
  5165. break;
  5166. default:
  5167. /* forward key to browser */
  5168. retval = false;
  5169. break;
  5170. }
  5171. }
  5172. if (_sapp_call_event(&_sapp.event)) {
  5173. /* consume event via sapp_consume_event() */
  5174. retval = true;
  5175. }
  5176. if (send_keyup_followup) {
  5177. _sapp.event.type = SAPP_EVENTTYPE_KEY_UP;
  5178. if (_sapp_call_event(&_sapp.event)) {
  5179. retval = true;
  5180. }
  5181. }
  5182. }
  5183. }
  5184. _sapp_emsc_update_keyboard_state();
  5185. _sapp_emsc_update_mouse_lock_state();
  5186. return retval;
  5187. }
  5188. _SOKOL_PRIVATE EM_BOOL _sapp_emsc_touch_cb(int emsc_type, const EmscriptenTouchEvent* emsc_event, void* user_data) {
  5189. _SOKOL_UNUSED(user_data);
  5190. bool retval = true;
  5191. if (_sapp_events_enabled()) {
  5192. sapp_event_type type;
  5193. switch (emsc_type) {
  5194. case EMSCRIPTEN_EVENT_TOUCHSTART:
  5195. type = SAPP_EVENTTYPE_TOUCHES_BEGAN;
  5196. break;
  5197. case EMSCRIPTEN_EVENT_TOUCHMOVE:
  5198. type = SAPP_EVENTTYPE_TOUCHES_MOVED;
  5199. break;
  5200. case EMSCRIPTEN_EVENT_TOUCHEND:
  5201. type = SAPP_EVENTTYPE_TOUCHES_ENDED;
  5202. break;
  5203. case EMSCRIPTEN_EVENT_TOUCHCANCEL:
  5204. type = SAPP_EVENTTYPE_TOUCHES_CANCELLED;
  5205. break;
  5206. default:
  5207. type = SAPP_EVENTTYPE_INVALID;
  5208. retval = false;
  5209. break;
  5210. }
  5211. if (type != SAPP_EVENTTYPE_INVALID) {
  5212. _sapp_init_event(type);
  5213. _sapp.event.modifiers = _sapp_emsc_touch_event_mods(emsc_event);
  5214. _sapp.event.num_touches = emsc_event->numTouches;
  5215. if (_sapp.event.num_touches > SAPP_MAX_TOUCHPOINTS) {
  5216. _sapp.event.num_touches = SAPP_MAX_TOUCHPOINTS;
  5217. }
  5218. for (int i = 0; i < _sapp.event.num_touches; i++) {
  5219. const EmscriptenTouchPoint* src = &emsc_event->touches[i];
  5220. sapp_touchpoint* dst = &_sapp.event.touches[i];
  5221. dst->identifier = (uintptr_t)src->identifier;
  5222. dst->pos_x = src->targetX * _sapp.dpi_scale;
  5223. dst->pos_y = src->targetY * _sapp.dpi_scale;
  5224. dst->changed = src->isChanged;
  5225. }
  5226. _sapp_call_event(&_sapp.event);
  5227. }
  5228. }
  5229. _sapp_emsc_update_keyboard_state();
  5230. return retval;
  5231. }
  5232. _SOKOL_PRIVATE void _sapp_emsc_keytable_init(void) {
  5233. _sapp.keycodes[8] = SAPP_KEYCODE_BACKSPACE;
  5234. _sapp.keycodes[9] = SAPP_KEYCODE_TAB;
  5235. _sapp.keycodes[13] = SAPP_KEYCODE_ENTER;
  5236. _sapp.keycodes[16] = SAPP_KEYCODE_LEFT_SHIFT;
  5237. _sapp.keycodes[17] = SAPP_KEYCODE_LEFT_CONTROL;
  5238. _sapp.keycodes[18] = SAPP_KEYCODE_LEFT_ALT;
  5239. _sapp.keycodes[19] = SAPP_KEYCODE_PAUSE;
  5240. _sapp.keycodes[27] = SAPP_KEYCODE_ESCAPE;
  5241. _sapp.keycodes[32] = SAPP_KEYCODE_SPACE;
  5242. _sapp.keycodes[33] = SAPP_KEYCODE_PAGE_UP;
  5243. _sapp.keycodes[34] = SAPP_KEYCODE_PAGE_DOWN;
  5244. _sapp.keycodes[35] = SAPP_KEYCODE_END;
  5245. _sapp.keycodes[36] = SAPP_KEYCODE_HOME;
  5246. _sapp.keycodes[37] = SAPP_KEYCODE_LEFT;
  5247. _sapp.keycodes[38] = SAPP_KEYCODE_UP;
  5248. _sapp.keycodes[39] = SAPP_KEYCODE_RIGHT;
  5249. _sapp.keycodes[40] = SAPP_KEYCODE_DOWN;
  5250. _sapp.keycodes[45] = SAPP_KEYCODE_INSERT;
  5251. _sapp.keycodes[46] = SAPP_KEYCODE_DELETE;
  5252. _sapp.keycodes[48] = SAPP_KEYCODE_0;
  5253. _sapp.keycodes[49] = SAPP_KEYCODE_1;
  5254. _sapp.keycodes[50] = SAPP_KEYCODE_2;
  5255. _sapp.keycodes[51] = SAPP_KEYCODE_3;
  5256. _sapp.keycodes[52] = SAPP_KEYCODE_4;
  5257. _sapp.keycodes[53] = SAPP_KEYCODE_5;
  5258. _sapp.keycodes[54] = SAPP_KEYCODE_6;
  5259. _sapp.keycodes[55] = SAPP_KEYCODE_7;
  5260. _sapp.keycodes[56] = SAPP_KEYCODE_8;
  5261. _sapp.keycodes[57] = SAPP_KEYCODE_9;
  5262. _sapp.keycodes[59] = SAPP_KEYCODE_SEMICOLON;
  5263. _sapp.keycodes[64] = SAPP_KEYCODE_EQUAL;
  5264. _sapp.keycodes[65] = SAPP_KEYCODE_A;
  5265. _sapp.keycodes[66] = SAPP_KEYCODE_B;
  5266. _sapp.keycodes[67] = SAPP_KEYCODE_C;
  5267. _sapp.keycodes[68] = SAPP_KEYCODE_D;
  5268. _sapp.keycodes[69] = SAPP_KEYCODE_E;
  5269. _sapp.keycodes[70] = SAPP_KEYCODE_F;
  5270. _sapp.keycodes[71] = SAPP_KEYCODE_G;
  5271. _sapp.keycodes[72] = SAPP_KEYCODE_H;
  5272. _sapp.keycodes[73] = SAPP_KEYCODE_I;
  5273. _sapp.keycodes[74] = SAPP_KEYCODE_J;
  5274. _sapp.keycodes[75] = SAPP_KEYCODE_K;
  5275. _sapp.keycodes[76] = SAPP_KEYCODE_L;
  5276. _sapp.keycodes[77] = SAPP_KEYCODE_M;
  5277. _sapp.keycodes[78] = SAPP_KEYCODE_N;
  5278. _sapp.keycodes[79] = SAPP_KEYCODE_O;
  5279. _sapp.keycodes[80] = SAPP_KEYCODE_P;
  5280. _sapp.keycodes[81] = SAPP_KEYCODE_Q;
  5281. _sapp.keycodes[82] = SAPP_KEYCODE_R;
  5282. _sapp.keycodes[83] = SAPP_KEYCODE_S;
  5283. _sapp.keycodes[84] = SAPP_KEYCODE_T;
  5284. _sapp.keycodes[85] = SAPP_KEYCODE_U;
  5285. _sapp.keycodes[86] = SAPP_KEYCODE_V;
  5286. _sapp.keycodes[87] = SAPP_KEYCODE_W;
  5287. _sapp.keycodes[88] = SAPP_KEYCODE_X;
  5288. _sapp.keycodes[89] = SAPP_KEYCODE_Y;
  5289. _sapp.keycodes[90] = SAPP_KEYCODE_Z;
  5290. _sapp.keycodes[91] = SAPP_KEYCODE_LEFT_SUPER;
  5291. _sapp.keycodes[93] = SAPP_KEYCODE_MENU;
  5292. _sapp.keycodes[96] = SAPP_KEYCODE_KP_0;
  5293. _sapp.keycodes[97] = SAPP_KEYCODE_KP_1;
  5294. _sapp.keycodes[98] = SAPP_KEYCODE_KP_2;
  5295. _sapp.keycodes[99] = SAPP_KEYCODE_KP_3;
  5296. _sapp.keycodes[100] = SAPP_KEYCODE_KP_4;
  5297. _sapp.keycodes[101] = SAPP_KEYCODE_KP_5;
  5298. _sapp.keycodes[102] = SAPP_KEYCODE_KP_6;
  5299. _sapp.keycodes[103] = SAPP_KEYCODE_KP_7;
  5300. _sapp.keycodes[104] = SAPP_KEYCODE_KP_8;
  5301. _sapp.keycodes[105] = SAPP_KEYCODE_KP_9;
  5302. _sapp.keycodes[106] = SAPP_KEYCODE_KP_MULTIPLY;
  5303. _sapp.keycodes[107] = SAPP_KEYCODE_KP_ADD;
  5304. _sapp.keycodes[109] = SAPP_KEYCODE_KP_SUBTRACT;
  5305. _sapp.keycodes[110] = SAPP_KEYCODE_KP_DECIMAL;
  5306. _sapp.keycodes[111] = SAPP_KEYCODE_KP_DIVIDE;
  5307. _sapp.keycodes[112] = SAPP_KEYCODE_F1;
  5308. _sapp.keycodes[113] = SAPP_KEYCODE_F2;
  5309. _sapp.keycodes[114] = SAPP_KEYCODE_F3;
  5310. _sapp.keycodes[115] = SAPP_KEYCODE_F4;
  5311. _sapp.keycodes[116] = SAPP_KEYCODE_F5;
  5312. _sapp.keycodes[117] = SAPP_KEYCODE_F6;
  5313. _sapp.keycodes[118] = SAPP_KEYCODE_F7;
  5314. _sapp.keycodes[119] = SAPP_KEYCODE_F8;
  5315. _sapp.keycodes[120] = SAPP_KEYCODE_F9;
  5316. _sapp.keycodes[121] = SAPP_KEYCODE_F10;
  5317. _sapp.keycodes[122] = SAPP_KEYCODE_F11;
  5318. _sapp.keycodes[123] = SAPP_KEYCODE_F12;
  5319. _sapp.keycodes[144] = SAPP_KEYCODE_NUM_LOCK;
  5320. _sapp.keycodes[145] = SAPP_KEYCODE_SCROLL_LOCK;
  5321. _sapp.keycodes[173] = SAPP_KEYCODE_MINUS;
  5322. _sapp.keycodes[186] = SAPP_KEYCODE_SEMICOLON;
  5323. _sapp.keycodes[187] = SAPP_KEYCODE_EQUAL;
  5324. _sapp.keycodes[188] = SAPP_KEYCODE_COMMA;
  5325. _sapp.keycodes[189] = SAPP_KEYCODE_MINUS;
  5326. _sapp.keycodes[190] = SAPP_KEYCODE_PERIOD;
  5327. _sapp.keycodes[191] = SAPP_KEYCODE_SLASH;
  5328. _sapp.keycodes[192] = SAPP_KEYCODE_GRAVE_ACCENT;
  5329. _sapp.keycodes[219] = SAPP_KEYCODE_LEFT_BRACKET;
  5330. _sapp.keycodes[220] = SAPP_KEYCODE_BACKSLASH;
  5331. _sapp.keycodes[221] = SAPP_KEYCODE_RIGHT_BRACKET;
  5332. _sapp.keycodes[222] = SAPP_KEYCODE_APOSTROPHE;
  5333. _sapp.keycodes[224] = SAPP_KEYCODE_LEFT_SUPER;
  5334. }
  5335. #if defined(SOKOL_GLES2) || defined(SOKOL_GLES3)
  5336. _SOKOL_PRIVATE EM_BOOL _sapp_emsc_webgl_context_cb(int emsc_type, const void* reserved, void* user_data) {
  5337. _SOKOL_UNUSED(reserved);
  5338. _SOKOL_UNUSED(user_data);
  5339. sapp_event_type type;
  5340. switch (emsc_type) {
  5341. case EMSCRIPTEN_EVENT_WEBGLCONTEXTLOST: type = SAPP_EVENTTYPE_SUSPENDED; break;
  5342. case EMSCRIPTEN_EVENT_WEBGLCONTEXTRESTORED: type = SAPP_EVENTTYPE_RESUMED; break;
  5343. default: type = SAPP_EVENTTYPE_INVALID; break;
  5344. }
  5345. if (_sapp_events_enabled() && (SAPP_EVENTTYPE_INVALID != type)) {
  5346. _sapp_init_event(type);
  5347. _sapp_call_event(&_sapp.event);
  5348. }
  5349. return true;
  5350. }
  5351. _SOKOL_PRIVATE void _sapp_emsc_webgl_init(void) {
  5352. EmscriptenWebGLContextAttributes attrs;
  5353. emscripten_webgl_init_context_attributes(&attrs);
  5354. attrs.alpha = _sapp.desc.alpha;
  5355. attrs.depth = true;
  5356. attrs.stencil = true;
  5357. attrs.antialias = _sapp.sample_count > 1;
  5358. attrs.premultipliedAlpha = _sapp.desc.html5_premultiplied_alpha;
  5359. attrs.preserveDrawingBuffer = _sapp.desc.html5_preserve_drawing_buffer;
  5360. attrs.enableExtensionsByDefault = true;
  5361. #if defined(SOKOL_GLES3)
  5362. if (_sapp.desc.gl_force_gles2) {
  5363. attrs.majorVersion = 1;
  5364. _sapp.gles2_fallback = true;
  5365. }
  5366. else {
  5367. attrs.majorVersion = 2;
  5368. }
  5369. #endif
  5370. EMSCRIPTEN_WEBGL_CONTEXT_HANDLE ctx = emscripten_webgl_create_context(_sapp.html5_canvas_selector, &attrs);
  5371. if (!ctx) {
  5372. attrs.majorVersion = 1;
  5373. ctx = emscripten_webgl_create_context(_sapp.html5_canvas_selector, &attrs);
  5374. _sapp.gles2_fallback = true;
  5375. }
  5376. emscripten_webgl_make_context_current(ctx);
  5377. /* some WebGL extension are not enabled automatically by emscripten */
  5378. emscripten_webgl_enable_extension(ctx, "WEBKIT_WEBGL_compressed_texture_pvrtc");
  5379. }
  5380. #endif
  5381. #if defined(SOKOL_WGPU)
  5382. #define _SAPP_EMSC_WGPU_STATE_INITIAL (0)
  5383. #define _SAPP_EMSC_WGPU_STATE_READY (1)
  5384. #define _SAPP_EMSC_WGPU_STATE_RUNNING (2)
  5385. #if defined(__cplusplus)
  5386. extern "C" {
  5387. #endif
  5388. /* called when the asynchronous WebGPU device + swapchain init code in JS has finished */
  5389. EMSCRIPTEN_KEEPALIVE void _sapp_emsc_wgpu_ready(int device_id, int swapchain_id, int swapchain_fmt) {
  5390. SOKOL_ASSERT(0 == _sapp.emsc.wgpu.device);
  5391. _sapp.emsc.wgpu.device = (WGPUDevice) device_id;
  5392. _sapp.emsc.wgpu.swapchain = (WGPUSwapChain) swapchain_id;
  5393. _sapp.emsc.wgpu.render_format = (WGPUTextureFormat) swapchain_fmt;
  5394. _sapp.emsc.wgpu.state = _SAPP_EMSC_WGPU_STATE_READY;
  5395. }
  5396. #if defined(__cplusplus)
  5397. } // extern "C"
  5398. #endif
  5399. /* embedded JS function to handle all the asynchronous WebGPU setup */
  5400. EM_JS(void, sapp_js_wgpu_init, (), {
  5401. WebGPU.initManagers();
  5402. // FIXME: the extension activation must be more clever here
  5403. navigator.gpu.requestAdapter().then(function(adapter) {
  5404. console.log("wgpu adapter extensions: " + adapter.extensions);
  5405. adapter.requestDevice({ extensions: ["textureCompressionBC"]}).then(function(device) {
  5406. var gpuContext = document.getElementById("canvas").getContext("gpupresent");
  5407. console.log("wgpu device extensions: " + adapter.extensions);
  5408. gpuContext.getSwapChainPreferredFormat(device).then(function(fmt) {
  5409. var swapChainDescriptor = { device: device, format: fmt };
  5410. var swapChain = gpuContext.configureSwapChain(swapChainDescriptor);
  5411. var deviceId = WebGPU.mgrDevice.create(device);
  5412. var swapChainId = WebGPU.mgrSwapChain.create(swapChain);
  5413. var fmtId = WebGPU.TextureFormat.findIndex(function(elm) { return elm==fmt; });
  5414. console.log("wgpu device: " + device);
  5415. console.log("wgpu swap chain: " + swapChain);
  5416. console.log("wgpu preferred format: " + fmt + " (" + fmtId + ")");
  5417. __sapp_emsc_wgpu_ready(deviceId, swapChainId, fmtId);
  5418. });
  5419. });
  5420. });
  5421. });
  5422. _SOKOL_PRIVATE void _sapp_emsc_wgpu_surfaces_create(void) {
  5423. SOKOL_ASSERT(_sapp.emsc.wgpu.device);
  5424. SOKOL_ASSERT(_sapp.emsc.wgpu.swapchain);
  5425. SOKOL_ASSERT(0 == _sapp.emsc.wgpu.depth_stencil_tex);
  5426. SOKOL_ASSERT(0 == _sapp.emsc.wgpu.depth_stencil_view);
  5427. SOKOL_ASSERT(0 == _sapp.emsc.wgpu.msaa_tex);
  5428. SOKOL_ASSERT(0 == _sapp.emsc.wgpu.msaa_view);
  5429. WGPUTextureDescriptor ds_desc;
  5430. memset(&ds_desc, 0, sizeof(ds_desc));
  5431. ds_desc.usage = WGPUTextureUsage_OutputAttachment;
  5432. ds_desc.dimension = WGPUTextureDimension_2D;
  5433. ds_desc.size.width = (uint32_t) _sapp.framebuffer_width;
  5434. ds_desc.size.height = (uint32_t) _sapp.framebuffer_height;
  5435. ds_desc.size.depth = 1;
  5436. ds_desc.arrayLayerCount = 1;
  5437. ds_desc.format = WGPUTextureFormat_Depth24PlusStencil8;
  5438. ds_desc.mipLevelCount = 1;
  5439. ds_desc.sampleCount = _sapp.sample_count;
  5440. _sapp.emsc.wgpu.depth_stencil_tex = wgpuDeviceCreateTexture(_sapp.emsc.wgpu.device, &ds_desc);
  5441. _sapp.emsc.wgpu.depth_stencil_view = wgpuTextureCreateView(_sapp.emsc.wgpu.depth_stencil_tex, 0);
  5442. if (_sapp.sample_count > 1) {
  5443. WGPUTextureDescriptor msaa_desc;
  5444. memset(&msaa_desc, 0, sizeof(msaa_desc));
  5445. msaa_desc.usage = WGPUTextureUsage_OutputAttachment;
  5446. msaa_desc.dimension = WGPUTextureDimension_2D;
  5447. msaa_desc.size.width = (uint32_t) _sapp.framebuffer_width;
  5448. msaa_desc.size.height = (uint32_t) _sapp.framebuffer_height;
  5449. msaa_desc.size.depth = 1;
  5450. msaa_desc.arrayLayerCount = 1;
  5451. msaa_desc.format = _sapp.emsc.wgpu.render_format;
  5452. msaa_desc.mipLevelCount = 1;
  5453. msaa_desc.sampleCount = _sapp.sample_count;
  5454. _sapp.emsc.wgpu.msaa_tex = wgpuDeviceCreateTexture(_sapp.emsc.wgpu.device, &msaa_desc);
  5455. _sapp.emsc.wgpu.msaa_view = wgpuTextureCreateView(_sapp.emsc.wgpu.msaa_tex, 0);
  5456. }
  5457. }
  5458. _SOKOL_PRIVATE void _sapp_emsc_wgpu_surfaces_discard(void) {
  5459. if (_sapp.emsc.wgpu.msaa_tex) {
  5460. wgpuTextureRelease(_sapp.emsc.wgpu.msaa_tex);
  5461. _sapp.emsc.wgpu.msaa_tex = 0;
  5462. }
  5463. if (_sapp.emsc.wgpu.msaa_view) {
  5464. wgpuTextureViewRelease(_sapp.emsc.wgpu.msaa_view);
  5465. _sapp.emsc.wgpu.msaa_view = 0;
  5466. }
  5467. if (_sapp.emsc.wgpu.depth_stencil_tex) {
  5468. wgpuTextureRelease(_sapp.emsc.wgpu.depth_stencil_tex);
  5469. _sapp.emsc.wgpu.depth_stencil_tex = 0;
  5470. }
  5471. if (_sapp.emsc.wgpu.depth_stencil_view) {
  5472. wgpuTextureViewRelease(_sapp.emsc.wgpu.depth_stencil_view);
  5473. _sapp.emsc.wgpu.depth_stencil_view = 0;
  5474. }
  5475. }
  5476. _SOKOL_PRIVATE void _sapp_emsc_wgpu_next_frame(void) {
  5477. if (_sapp.emsc.wgpu.swapchain_view) {
  5478. wgpuTextureViewRelease(_sapp.emsc.wgpu.swapchain_view);
  5479. }
  5480. _sapp.emsc.wgpu.swapchain_view = wgpuSwapChainGetCurrentTextureView(_sapp.emsc.wgpu.swapchain);
  5481. }
  5482. #endif
  5483. _SOKOL_PRIVATE void _sapp_emsc_register_eventhandlers(void) {
  5484. emscripten_set_mousedown_callback(_sapp.html5_canvas_selector, 0, true, _sapp_emsc_mouse_cb);
  5485. emscripten_set_mouseup_callback(_sapp.html5_canvas_selector, 0, true, _sapp_emsc_mouse_cb);
  5486. emscripten_set_mousemove_callback(_sapp.html5_canvas_selector, 0, true, _sapp_emsc_mouse_cb);
  5487. emscripten_set_mouseenter_callback(_sapp.html5_canvas_selector, 0, true, _sapp_emsc_mouse_cb);
  5488. emscripten_set_mouseleave_callback(_sapp.html5_canvas_selector, 0, true, _sapp_emsc_mouse_cb);
  5489. emscripten_set_wheel_callback(_sapp.html5_canvas_selector, 0, true, _sapp_emsc_wheel_cb);
  5490. emscripten_set_keydown_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, 0, true, _sapp_emsc_key_cb);
  5491. emscripten_set_keyup_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, 0, true, _sapp_emsc_key_cb);
  5492. emscripten_set_keypress_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, 0, true, _sapp_emsc_key_cb);
  5493. emscripten_set_touchstart_callback(_sapp.html5_canvas_selector, 0, true, _sapp_emsc_touch_cb);
  5494. emscripten_set_touchmove_callback(_sapp.html5_canvas_selector, 0, true, _sapp_emsc_touch_cb);
  5495. emscripten_set_touchend_callback(_sapp.html5_canvas_selector, 0, true, _sapp_emsc_touch_cb);
  5496. emscripten_set_touchcancel_callback(_sapp.html5_canvas_selector, 0, true, _sapp_emsc_touch_cb);
  5497. emscripten_set_pointerlockchange_callback(EMSCRIPTEN_EVENT_TARGET_DOCUMENT, 0, true, _sapp_emsc_pointerlockchange_cb);
  5498. emscripten_set_pointerlockerror_callback(EMSCRIPTEN_EVENT_TARGET_DOCUMENT, 0, true, _sapp_emsc_pointerlockerror_cb);
  5499. sapp_js_add_beforeunload_listener();
  5500. if (_sapp.clipboard.enabled) {
  5501. sapp_js_add_clipboard_listener();
  5502. }
  5503. if (_sapp.drop.enabled) {
  5504. sapp_js_add_dragndrop_listeners(&_sapp.html5_canvas_selector[1]);
  5505. }
  5506. #if defined(SOKOL_GLES2) || defined(SOKOL_GLES3)
  5507. emscripten_set_webglcontextlost_callback(_sapp.html5_canvas_selector, 0, true, _sapp_emsc_webgl_context_cb);
  5508. emscripten_set_webglcontextrestored_callback(_sapp.html5_canvas_selector, 0, true, _sapp_emsc_webgl_context_cb);
  5509. #endif
  5510. }
  5511. _SOKOL_PRIVATE void _sapp_emsc_unregister_eventhandlers() {
  5512. emscripten_set_mousedown_callback(_sapp.html5_canvas_selector, 0, true, 0);
  5513. emscripten_set_mouseup_callback(_sapp.html5_canvas_selector, 0, true, 0);
  5514. emscripten_set_mousemove_callback(_sapp.html5_canvas_selector, 0, true, 0);
  5515. emscripten_set_mouseenter_callback(_sapp.html5_canvas_selector, 0, true, 0);
  5516. emscripten_set_mouseleave_callback(_sapp.html5_canvas_selector, 0, true, 0);
  5517. emscripten_set_wheel_callback(_sapp.html5_canvas_selector, 0, true, 0);
  5518. emscripten_set_keydown_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, 0, true, 0);
  5519. emscripten_set_keyup_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, 0, true, 0);
  5520. emscripten_set_keypress_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, 0, true, 0);
  5521. emscripten_set_touchstart_callback(_sapp.html5_canvas_selector, 0, true, 0);
  5522. emscripten_set_touchmove_callback(_sapp.html5_canvas_selector, 0, true, 0);
  5523. emscripten_set_touchend_callback(_sapp.html5_canvas_selector, 0, true, 0);
  5524. emscripten_set_touchcancel_callback(_sapp.html5_canvas_selector, 0, true, 0);
  5525. emscripten_set_pointerlockchange_callback(EMSCRIPTEN_EVENT_TARGET_DOCUMENT, 0, true, 0);
  5526. emscripten_set_pointerlockerror_callback(EMSCRIPTEN_EVENT_TARGET_DOCUMENT, 0, true, 0);
  5527. sapp_js_remove_beforeunload_listener();
  5528. if (_sapp.clipboard.enabled) {
  5529. sapp_js_remove_clipboard_listener();
  5530. }
  5531. if (_sapp.drop.enabled) {
  5532. sapp_js_remove_dragndrop_listeners(&_sapp.html5_canvas_selector[1]);
  5533. }
  5534. #if defined(SOKOL_GLES2) || defined(SOKOL_GLES3)
  5535. emscripten_set_webglcontextlost_callback(_sapp.html5_canvas_selector, 0, true, 0);
  5536. emscripten_set_webglcontextrestored_callback(_sapp.html5_canvas_selector, 0, true, 0);
  5537. #endif
  5538. }
  5539. _SOKOL_PRIVATE EM_BOOL _sapp_emsc_frame(double time, void* userData) {
  5540. _SOKOL_UNUSED(time);
  5541. _SOKOL_UNUSED(userData);
  5542. #if defined(SOKOL_WGPU)
  5543. /*
  5544. on WebGPU, the emscripten frame callback will already be called while
  5545. the asynchronous WebGPU device and swapchain initialization is still
  5546. in progress
  5547. */
  5548. switch (_sapp.emsc.wgpu.state) {
  5549. case _SAPP_EMSC_WGPU_STATE_INITIAL:
  5550. /* async JS init hasn't finished yet */
  5551. break;
  5552. case _SAPP_EMSC_WGPU_STATE_READY:
  5553. /* perform post-async init stuff */
  5554. _sapp_emsc_wgpu_surfaces_create();
  5555. _sapp.emsc.wgpu.state = _SAPP_EMSC_WGPU_STATE_RUNNING;
  5556. break;
  5557. case _SAPP_EMSC_WGPU_STATE_RUNNING:
  5558. /* a regular frame */
  5559. _sapp_emsc_wgpu_next_frame();
  5560. _sapp_frame();
  5561. break;
  5562. }
  5563. #else
  5564. /* WebGL code path */
  5565. _sapp_frame();
  5566. #endif
  5567. /* quit-handling */
  5568. if (_sapp.quit_requested) {
  5569. _sapp_init_event(SAPP_EVENTTYPE_QUIT_REQUESTED);
  5570. _sapp_call_event(&_sapp.event);
  5571. if (_sapp.quit_requested) {
  5572. _sapp.quit_ordered = true;
  5573. }
  5574. }
  5575. if (_sapp.quit_ordered) {
  5576. _sapp_emsc_unregister_eventhandlers();
  5577. _sapp_call_cleanup();
  5578. _sapp_discard_state();
  5579. return EM_FALSE;
  5580. }
  5581. return EM_TRUE;
  5582. }
  5583. _SOKOL_PRIVATE void _sapp_emsc_run(const sapp_desc* desc) {
  5584. _sapp_init_state(desc);
  5585. _sapp.emsc.canvas_selector[0] = '#';
  5586. _sapp_strcpy(_sapp.desc.html5.canvas_name, &_sapp.emsc.canvas_selector[1], sizeof(_sapp.emsc.canvas_selector) - 1);
  5587. _sapp.desc.html5.canvas_name = &_sapp.emsc.canvas_selector[1];
  5588. _sapp.emsc.ask_leave_site = _sapp.desc.html5.ask_leave_site;
  5589. sapp_js_pointer_init(&_sapp.html5_canvas_selector[1]);
  5590. _sapp_emsc_keytable_init();
  5591. double w, h;
  5592. if (_sapp.desc.html5_canvas_resize) {
  5593. w = (double) _sapp.desc.width;
  5594. h = (double) _sapp.desc.height;
  5595. }
  5596. else {
  5597. emscripten_get_element_css_size(_sapp.html5_canvas_selector, &w, &h);
  5598. emscripten_set_resize_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, 0, false, _sapp_emsc_size_changed);
  5599. }
  5600. if (_sapp.desc.high_dpi) {
  5601. _sapp.dpi_scale = emscripten_get_device_pixel_ratio();
  5602. }
  5603. _sapp.window_width = (int) w;
  5604. _sapp.window_height = (int) h;
  5605. _sapp.framebuffer_width = (int) (w * _sapp.dpi_scale);
  5606. _sapp.framebuffer_height = (int) (h * _sapp.dpi_scale);
  5607. emscripten_set_canvas_element_size(_sapp.html5_canvas_selector, _sapp.framebuffer_width, _sapp.framebuffer_height);
  5608. #if defined(SOKOL_GLES2) || defined(SOKOL_GLES3)
  5609. _sapp_emsc_webgl_init();
  5610. #elif defined(SOKOL_WGPU)
  5611. sapp_js_wgpu_init();
  5612. #endif
  5613. _sapp.valid = true;
  5614. _sapp_emsc_register_eventhandlers();
  5615. _sapp_set_icon(&desc->icon);
  5616. /* start the frame loop */
  5617. emscripten_request_animation_frame_loop(_sapp_emsc_frame, 0);
  5618. /* NOT A BUG: do not call _sapp_discard_state() here, instead this is
  5619. called in _sapp_emsc_frame() when the application is ordered to quit
  5620. */
  5621. }
  5622. #if !defined(SOKOL_NO_ENTRY)
  5623. int main(int argc, char* argv[]) {
  5624. sapp_desc desc = sokol_main(argc, argv);
  5625. _sapp_emsc_run(&desc);
  5626. return 0;
  5627. }
  5628. #endif /* SOKOL_NO_ENTRY */
  5629. #endif /* _SAPP_EMSCRIPTEN */
  5630. /*== MISC GL SUPPORT FUNCTIONS ================================================*/
  5631. #if defined(SOKOL_GLCORE33)
  5632. typedef struct {
  5633. int red_bits;
  5634. int green_bits;
  5635. int blue_bits;
  5636. int alpha_bits;
  5637. int depth_bits;
  5638. int stencil_bits;
  5639. int samples;
  5640. bool doublebuffer;
  5641. uintptr_t handle;
  5642. } _sapp_gl_fbconfig;
  5643. _SOKOL_PRIVATE void _sapp_gl_init_fbconfig(_sapp_gl_fbconfig* fbconfig) {
  5644. memset(fbconfig, 0, sizeof(_sapp_gl_fbconfig));
  5645. /* -1 means "don't care" */
  5646. fbconfig->red_bits = -1;
  5647. fbconfig->green_bits = -1;
  5648. fbconfig->blue_bits = -1;
  5649. fbconfig->alpha_bits = -1;
  5650. fbconfig->depth_bits = -1;
  5651. fbconfig->stencil_bits = -1;
  5652. fbconfig->samples = -1;
  5653. }
  5654. _SOKOL_PRIVATE const _sapp_gl_fbconfig* _sapp_gl_choose_fbconfig(const _sapp_gl_fbconfig* desired, const _sapp_gl_fbconfig* alternatives, int count) {
  5655. int missing, least_missing = 1000000;
  5656. int color_diff, least_color_diff = 10000000;
  5657. int extra_diff, least_extra_diff = 10000000;
  5658. const _sapp_gl_fbconfig* current;
  5659. const _sapp_gl_fbconfig* closest = 0;
  5660. for (int i = 0; i < count; i++) {
  5661. current = alternatives + i;
  5662. if (desired->doublebuffer != current->doublebuffer) {
  5663. continue;
  5664. }
  5665. missing = 0;
  5666. if (desired->alpha_bits > 0 && current->alpha_bits == 0) {
  5667. missing++;
  5668. }
  5669. if (desired->depth_bits > 0 && current->depth_bits == 0) {
  5670. missing++;
  5671. }
  5672. if (desired->stencil_bits > 0 && current->stencil_bits == 0) {
  5673. missing++;
  5674. }
  5675. if (desired->samples > 0 && current->samples == 0) {
  5676. /* Technically, several multisampling buffers could be
  5677. involved, but that's a lower level implementation detail and
  5678. not important to us here, so we count them as one
  5679. */
  5680. missing++;
  5681. }
  5682. /* These polynomials make many small channel size differences matter
  5683. less than one large channel size difference
  5684. Calculate color channel size difference value
  5685. */
  5686. color_diff = 0;
  5687. if (desired->red_bits != -1) {
  5688. color_diff += (desired->red_bits - current->red_bits) * (desired->red_bits - current->red_bits);
  5689. }
  5690. if (desired->green_bits != -1) {
  5691. color_diff += (desired->green_bits - current->green_bits) * (desired->green_bits - current->green_bits);
  5692. }
  5693. if (desired->blue_bits != -1) {
  5694. color_diff += (desired->blue_bits - current->blue_bits) * (desired->blue_bits - current->blue_bits);
  5695. }
  5696. /* Calculate non-color channel size difference value */
  5697. extra_diff = 0;
  5698. if (desired->alpha_bits != -1) {
  5699. extra_diff += (desired->alpha_bits - current->alpha_bits) * (desired->alpha_bits - current->alpha_bits);
  5700. }
  5701. if (desired->depth_bits != -1) {
  5702. extra_diff += (desired->depth_bits - current->depth_bits) * (desired->depth_bits - current->depth_bits);
  5703. }
  5704. if (desired->stencil_bits != -1) {
  5705. extra_diff += (desired->stencil_bits - current->stencil_bits) * (desired->stencil_bits - current->stencil_bits);
  5706. }
  5707. if (desired->samples != -1) {
  5708. extra_diff += (desired->samples - current->samples) * (desired->samples - current->samples);
  5709. }
  5710. /* Figure out if the current one is better than the best one found so far
  5711. Least number of missing buffers is the most important heuristic,
  5712. then color buffer size match and lastly size match for other buffers
  5713. */
  5714. if (missing < least_missing) {
  5715. closest = current;
  5716. }
  5717. else if (missing == least_missing) {
  5718. if ((color_diff < least_color_diff) ||
  5719. (color_diff == least_color_diff && extra_diff < least_extra_diff))
  5720. {
  5721. closest = current;
  5722. }
  5723. }
  5724. if (current == closest) {
  5725. least_missing = missing;
  5726. least_color_diff = color_diff;
  5727. least_extra_diff = extra_diff;
  5728. }
  5729. }
  5730. return closest;
  5731. }
  5732. #endif
  5733. /*== WINDOWS DESKTOP and UWP====================================================*/
  5734. #if defined(_SAPP_WIN32) || defined(_SAPP_UWP)
  5735. _SOKOL_PRIVATE bool _sapp_win32_uwp_utf8_to_wide(const char* src, wchar_t* dst, int dst_num_bytes) {
  5736. SOKOL_ASSERT(src && dst && (dst_num_bytes > 1));
  5737. memset(dst, 0, (size_t)dst_num_bytes);
  5738. const int dst_chars = dst_num_bytes / (int)sizeof(wchar_t);
  5739. const int dst_needed = MultiByteToWideChar(CP_UTF8, 0, src, -1, 0, 0);
  5740. if ((dst_needed > 0) && (dst_needed < dst_chars)) {
  5741. MultiByteToWideChar(CP_UTF8, 0, src, -1, dst, dst_chars);
  5742. return true;
  5743. }
  5744. else {
  5745. /* input string doesn't fit into destination buffer */
  5746. return false;
  5747. }
  5748. }
  5749. _SOKOL_PRIVATE void _sapp_win32_uwp_app_event(sapp_event_type type) {
  5750. if (_sapp_events_enabled()) {
  5751. _sapp_init_event(type);
  5752. _sapp_call_event(&_sapp.event);
  5753. }
  5754. }
  5755. _SOKOL_PRIVATE void _sapp_win32_uwp_init_keytable(void) {
  5756. /* same as GLFW */
  5757. _sapp.keycodes[0x00B] = SAPP_KEYCODE_0;
  5758. _sapp.keycodes[0x002] = SAPP_KEYCODE_1;
  5759. _sapp.keycodes[0x003] = SAPP_KEYCODE_2;
  5760. _sapp.keycodes[0x004] = SAPP_KEYCODE_3;
  5761. _sapp.keycodes[0x005] = SAPP_KEYCODE_4;
  5762. _sapp.keycodes[0x006] = SAPP_KEYCODE_5;
  5763. _sapp.keycodes[0x007] = SAPP_KEYCODE_6;
  5764. _sapp.keycodes[0x008] = SAPP_KEYCODE_7;
  5765. _sapp.keycodes[0x009] = SAPP_KEYCODE_8;
  5766. _sapp.keycodes[0x00A] = SAPP_KEYCODE_9;
  5767. _sapp.keycodes[0x01E] = SAPP_KEYCODE_A;
  5768. _sapp.keycodes[0x030] = SAPP_KEYCODE_B;
  5769. _sapp.keycodes[0x02E] = SAPP_KEYCODE_C;
  5770. _sapp.keycodes[0x020] = SAPP_KEYCODE_D;
  5771. _sapp.keycodes[0x012] = SAPP_KEYCODE_E;
  5772. _sapp.keycodes[0x021] = SAPP_KEYCODE_F;
  5773. _sapp.keycodes[0x022] = SAPP_KEYCODE_G;
  5774. _sapp.keycodes[0x023] = SAPP_KEYCODE_H;
  5775. _sapp.keycodes[0x017] = SAPP_KEYCODE_I;
  5776. _sapp.keycodes[0x024] = SAPP_KEYCODE_J;
  5777. _sapp.keycodes[0x025] = SAPP_KEYCODE_K;
  5778. _sapp.keycodes[0x026] = SAPP_KEYCODE_L;
  5779. _sapp.keycodes[0x032] = SAPP_KEYCODE_M;
  5780. _sapp.keycodes[0x031] = SAPP_KEYCODE_N;
  5781. _sapp.keycodes[0x018] = SAPP_KEYCODE_O;
  5782. _sapp.keycodes[0x019] = SAPP_KEYCODE_P;
  5783. _sapp.keycodes[0x010] = SAPP_KEYCODE_Q;
  5784. _sapp.keycodes[0x013] = SAPP_KEYCODE_R;
  5785. _sapp.keycodes[0x01F] = SAPP_KEYCODE_S;
  5786. _sapp.keycodes[0x014] = SAPP_KEYCODE_T;
  5787. _sapp.keycodes[0x016] = SAPP_KEYCODE_U;
  5788. _sapp.keycodes[0x02F] = SAPP_KEYCODE_V;
  5789. _sapp.keycodes[0x011] = SAPP_KEYCODE_W;
  5790. _sapp.keycodes[0x02D] = SAPP_KEYCODE_X;
  5791. _sapp.keycodes[0x015] = SAPP_KEYCODE_Y;
  5792. _sapp.keycodes[0x02C] = SAPP_KEYCODE_Z;
  5793. _sapp.keycodes[0x028] = SAPP_KEYCODE_APOSTROPHE;
  5794. _sapp.keycodes[0x02B] = SAPP_KEYCODE_BACKSLASH;
  5795. _sapp.keycodes[0x033] = SAPP_KEYCODE_COMMA;
  5796. _sapp.keycodes[0x00D] = SAPP_KEYCODE_EQUAL;
  5797. _sapp.keycodes[0x029] = SAPP_KEYCODE_GRAVE_ACCENT;
  5798. _sapp.keycodes[0x01A] = SAPP_KEYCODE_LEFT_BRACKET;
  5799. _sapp.keycodes[0x00C] = SAPP_KEYCODE_MINUS;
  5800. _sapp.keycodes[0x034] = SAPP_KEYCODE_PERIOD;
  5801. _sapp.keycodes[0x01B] = SAPP_KEYCODE_RIGHT_BRACKET;
  5802. _sapp.keycodes[0x027] = SAPP_KEYCODE_SEMICOLON;
  5803. _sapp.keycodes[0x035] = SAPP_KEYCODE_SLASH;
  5804. _sapp.keycodes[0x056] = SAPP_KEYCODE_WORLD_2;
  5805. _sapp.keycodes[0x00E] = SAPP_KEYCODE_BACKSPACE;
  5806. _sapp.keycodes[0x153] = SAPP_KEYCODE_DELETE;
  5807. _sapp.keycodes[0x14F] = SAPP_KEYCODE_END;
  5808. _sapp.keycodes[0x01C] = SAPP_KEYCODE_ENTER;
  5809. _sapp.keycodes[0x001] = SAPP_KEYCODE_ESCAPE;
  5810. _sapp.keycodes[0x147] = SAPP_KEYCODE_HOME;
  5811. _sapp.keycodes[0x152] = SAPP_KEYCODE_INSERT;
  5812. _sapp.keycodes[0x15D] = SAPP_KEYCODE_MENU;
  5813. _sapp.keycodes[0x151] = SAPP_KEYCODE_PAGE_DOWN;
  5814. _sapp.keycodes[0x149] = SAPP_KEYCODE_PAGE_UP;
  5815. _sapp.keycodes[0x045] = SAPP_KEYCODE_PAUSE;
  5816. _sapp.keycodes[0x146] = SAPP_KEYCODE_PAUSE;
  5817. _sapp.keycodes[0x039] = SAPP_KEYCODE_SPACE;
  5818. _sapp.keycodes[0x00F] = SAPP_KEYCODE_TAB;
  5819. _sapp.keycodes[0x03A] = SAPP_KEYCODE_CAPS_LOCK;
  5820. _sapp.keycodes[0x145] = SAPP_KEYCODE_NUM_LOCK;
  5821. _sapp.keycodes[0x046] = SAPP_KEYCODE_SCROLL_LOCK;
  5822. _sapp.keycodes[0x03B] = SAPP_KEYCODE_F1;
  5823. _sapp.keycodes[0x03C] = SAPP_KEYCODE_F2;
  5824. _sapp.keycodes[0x03D] = SAPP_KEYCODE_F3;
  5825. _sapp.keycodes[0x03E] = SAPP_KEYCODE_F4;
  5826. _sapp.keycodes[0x03F] = SAPP_KEYCODE_F5;
  5827. _sapp.keycodes[0x040] = SAPP_KEYCODE_F6;
  5828. _sapp.keycodes[0x041] = SAPP_KEYCODE_F7;
  5829. _sapp.keycodes[0x042] = SAPP_KEYCODE_F8;
  5830. _sapp.keycodes[0x043] = SAPP_KEYCODE_F9;
  5831. _sapp.keycodes[0x044] = SAPP_KEYCODE_F10;
  5832. _sapp.keycodes[0x057] = SAPP_KEYCODE_F11;
  5833. _sapp.keycodes[0x058] = SAPP_KEYCODE_F12;
  5834. _sapp.keycodes[0x064] = SAPP_KEYCODE_F13;
  5835. _sapp.keycodes[0x065] = SAPP_KEYCODE_F14;
  5836. _sapp.keycodes[0x066] = SAPP_KEYCODE_F15;
  5837. _sapp.keycodes[0x067] = SAPP_KEYCODE_F16;
  5838. _sapp.keycodes[0x068] = SAPP_KEYCODE_F17;
  5839. _sapp.keycodes[0x069] = SAPP_KEYCODE_F18;
  5840. _sapp.keycodes[0x06A] = SAPP_KEYCODE_F19;
  5841. _sapp.keycodes[0x06B] = SAPP_KEYCODE_F20;
  5842. _sapp.keycodes[0x06C] = SAPP_KEYCODE_F21;
  5843. _sapp.keycodes[0x06D] = SAPP_KEYCODE_F22;
  5844. _sapp.keycodes[0x06E] = SAPP_KEYCODE_F23;
  5845. _sapp.keycodes[0x076] = SAPP_KEYCODE_F24;
  5846. _sapp.keycodes[0x038] = SAPP_KEYCODE_LEFT_ALT;
  5847. _sapp.keycodes[0x01D] = SAPP_KEYCODE_LEFT_CONTROL;
  5848. _sapp.keycodes[0x02A] = SAPP_KEYCODE_LEFT_SHIFT;
  5849. _sapp.keycodes[0x15B] = SAPP_KEYCODE_LEFT_SUPER;
  5850. _sapp.keycodes[0x137] = SAPP_KEYCODE_PRINT_SCREEN;
  5851. _sapp.keycodes[0x138] = SAPP_KEYCODE_RIGHT_ALT;
  5852. _sapp.keycodes[0x11D] = SAPP_KEYCODE_RIGHT_CONTROL;
  5853. _sapp.keycodes[0x036] = SAPP_KEYCODE_RIGHT_SHIFT;
  5854. _sapp.keycodes[0x15C] = SAPP_KEYCODE_RIGHT_SUPER;
  5855. _sapp.keycodes[0x150] = SAPP_KEYCODE_DOWN;
  5856. _sapp.keycodes[0x14B] = SAPP_KEYCODE_LEFT;
  5857. _sapp.keycodes[0x14D] = SAPP_KEYCODE_RIGHT;
  5858. _sapp.keycodes[0x148] = SAPP_KEYCODE_UP;
  5859. _sapp.keycodes[0x052] = SAPP_KEYCODE_KP_0;
  5860. _sapp.keycodes[0x04F] = SAPP_KEYCODE_KP_1;
  5861. _sapp.keycodes[0x050] = SAPP_KEYCODE_KP_2;
  5862. _sapp.keycodes[0x051] = SAPP_KEYCODE_KP_3;
  5863. _sapp.keycodes[0x04B] = SAPP_KEYCODE_KP_4;
  5864. _sapp.keycodes[0x04C] = SAPP_KEYCODE_KP_5;
  5865. _sapp.keycodes[0x04D] = SAPP_KEYCODE_KP_6;
  5866. _sapp.keycodes[0x047] = SAPP_KEYCODE_KP_7;
  5867. _sapp.keycodes[0x048] = SAPP_KEYCODE_KP_8;
  5868. _sapp.keycodes[0x049] = SAPP_KEYCODE_KP_9;
  5869. _sapp.keycodes[0x04E] = SAPP_KEYCODE_KP_ADD;
  5870. _sapp.keycodes[0x053] = SAPP_KEYCODE_KP_DECIMAL;
  5871. _sapp.keycodes[0x135] = SAPP_KEYCODE_KP_DIVIDE;
  5872. _sapp.keycodes[0x11C] = SAPP_KEYCODE_KP_ENTER;
  5873. _sapp.keycodes[0x037] = SAPP_KEYCODE_KP_MULTIPLY;
  5874. _sapp.keycodes[0x04A] = SAPP_KEYCODE_KP_SUBTRACT;
  5875. }
  5876. #endif // _SAPP_WIN32 || _SAPP_UWP
  5877. /*== WINDOWS DESKTOP===========================================================*/
  5878. #if defined(_SAPP_WIN32)
  5879. #if defined(SOKOL_D3D11)
  5880. #if defined(__cplusplus)
  5881. #define _sapp_d3d11_Release(self) (self)->Release()
  5882. #else
  5883. #define _sapp_d3d11_Release(self) (self)->lpVtbl->Release(self)
  5884. #endif
  5885. #define _SAPP_SAFE_RELEASE(obj) if (obj) { _sapp_d3d11_Release(obj); obj=0; }
  5886. static inline HRESULT _sapp_dxgi_GetBuffer(IDXGISwapChain* self, UINT Buffer, REFIID riid, void** ppSurface) {
  5887. #if defined(__cplusplus)
  5888. return self->GetBuffer(Buffer, riid, ppSurface);
  5889. #else
  5890. return self->lpVtbl->GetBuffer(self, Buffer, riid, ppSurface);
  5891. #endif
  5892. }
  5893. static inline HRESULT _sapp_d3d11_CreateRenderTargetView(ID3D11Device* self, ID3D11Resource *pResource, const D3D11_RENDER_TARGET_VIEW_DESC* pDesc, ID3D11RenderTargetView** ppRTView) {
  5894. #if defined(__cplusplus)
  5895. return self->CreateRenderTargetView(pResource, pDesc, ppRTView);
  5896. #else
  5897. return self->lpVtbl->CreateRenderTargetView(self, pResource, pDesc, ppRTView);
  5898. #endif
  5899. }
  5900. static inline HRESULT _sapp_d3d11_CreateTexture2D(ID3D11Device* self, const D3D11_TEXTURE2D_DESC* pDesc, const D3D11_SUBRESOURCE_DATA* pInitialData, ID3D11Texture2D** ppTexture2D) {
  5901. #if defined(__cplusplus)
  5902. return self->CreateTexture2D(pDesc, pInitialData, ppTexture2D);
  5903. #else
  5904. return self->lpVtbl->CreateTexture2D(self, pDesc, pInitialData, ppTexture2D);
  5905. #endif
  5906. }
  5907. static inline HRESULT _sapp_d3d11_CreateDepthStencilView(ID3D11Device* self, ID3D11Resource* pResource, const D3D11_DEPTH_STENCIL_VIEW_DESC* pDesc, ID3D11DepthStencilView** ppDepthStencilView) {
  5908. #if defined(__cplusplus)
  5909. return self->CreateDepthStencilView(pResource, pDesc, ppDepthStencilView);
  5910. #else
  5911. return self->lpVtbl->CreateDepthStencilView(self, pResource, pDesc, ppDepthStencilView);
  5912. #endif
  5913. }
  5914. static inline void _sapp_d3d11_ResolveSubresource(ID3D11DeviceContext* self, ID3D11Resource* pDstResource, UINT DstSubresource, ID3D11Resource* pSrcResource, UINT SrcSubresource, DXGI_FORMAT Format) {
  5915. #if defined(__cplusplus)
  5916. self->ResolveSubresource(pDstResource, DstSubresource, pSrcResource, SrcSubresource, Format);
  5917. #else
  5918. self->lpVtbl->ResolveSubresource(self, pDstResource, DstSubresource, pSrcResource, SrcSubresource, Format);
  5919. #endif
  5920. }
  5921. static inline HRESULT _sapp_dxgi_ResizeBuffers(IDXGISwapChain* self, UINT BufferCount, UINT Width, UINT Height, DXGI_FORMAT NewFormat, UINT SwapChainFlags) {
  5922. #if defined(__cplusplus)
  5923. return self->ResizeBuffers(BufferCount, Width, Height, NewFormat, SwapChainFlags);
  5924. #else
  5925. return self->lpVtbl->ResizeBuffers(self, BufferCount, Width, Height, NewFormat, SwapChainFlags);
  5926. #endif
  5927. }
  5928. static inline HRESULT _sapp_dxgi_Present(IDXGISwapChain* self, UINT SyncInterval, UINT Flags) {
  5929. #if defined(__cplusplus)
  5930. return self->Present(SyncInterval, Flags);
  5931. #else
  5932. return self->lpVtbl->Present(self, SyncInterval, Flags);
  5933. #endif
  5934. }
  5935. _SOKOL_PRIVATE void _sapp_d3d11_create_device_and_swapchain(void) {
  5936. DXGI_SWAP_CHAIN_DESC* sc_desc = &_sapp.d3d11.swap_chain_desc;
  5937. sc_desc->BufferDesc.Width = (UINT)_sapp.framebuffer_width;
  5938. sc_desc->BufferDesc.Height = (UINT)_sapp.framebuffer_height;
  5939. sc_desc->BufferDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
  5940. sc_desc->BufferDesc.RefreshRate.Numerator = 60;
  5941. sc_desc->BufferDesc.RefreshRate.Denominator = 1;
  5942. sc_desc->OutputWindow = _sapp.win32.hwnd;
  5943. sc_desc->Windowed = true;
  5944. if (_sapp.win32.is_win10_or_greater) {
  5945. sc_desc->BufferCount = 2;
  5946. sc_desc->SwapEffect = (DXGI_SWAP_EFFECT) _SAPP_DXGI_SWAP_EFFECT_FLIP_DISCARD;
  5947. }
  5948. else {
  5949. sc_desc->BufferCount = 1;
  5950. sc_desc->SwapEffect = DXGI_SWAP_EFFECT_DISCARD;
  5951. }
  5952. sc_desc->SampleDesc.Count = 1;
  5953. sc_desc->SampleDesc.Quality = 0;
  5954. sc_desc->BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
  5955. UINT create_flags = D3D11_CREATE_DEVICE_SINGLETHREADED | D3D11_CREATE_DEVICE_BGRA_SUPPORT;
  5956. #if defined(SOKOL_DEBUG)
  5957. create_flags |= D3D11_CREATE_DEVICE_DEBUG;
  5958. #endif
  5959. D3D_FEATURE_LEVEL feature_level;
  5960. HRESULT hr = D3D11CreateDeviceAndSwapChain(
  5961. NULL, /* pAdapter (use default) */
  5962. D3D_DRIVER_TYPE_HARDWARE, /* DriverType */
  5963. NULL, /* Software */
  5964. create_flags, /* Flags */
  5965. NULL, /* pFeatureLevels */
  5966. 0, /* FeatureLevels */
  5967. D3D11_SDK_VERSION, /* SDKVersion */
  5968. sc_desc, /* pSwapChainDesc */
  5969. &_sapp.d3d11.swap_chain, /* ppSwapChain */
  5970. &_sapp.d3d11.device, /* ppDevice */
  5971. &feature_level, /* pFeatureLevel */
  5972. &_sapp.d3d11.device_context); /* ppImmediateContext */
  5973. _SOKOL_UNUSED(hr);
  5974. SOKOL_ASSERT(SUCCEEDED(hr) && _sapp.d3d11.swap_chain && _sapp.d3d11.device && _sapp.d3d11.device_context);
  5975. }
  5976. _SOKOL_PRIVATE void _sapp_d3d11_destroy_device_and_swapchain(void) {
  5977. _SAPP_SAFE_RELEASE(_sapp.d3d11.swap_chain);
  5978. _SAPP_SAFE_RELEASE(_sapp.d3d11.device_context);
  5979. _SAPP_SAFE_RELEASE(_sapp.d3d11.device);
  5980. }
  5981. _SOKOL_PRIVATE void _sapp_d3d11_create_default_render_target(void) {
  5982. SOKOL_ASSERT(0 == _sapp.d3d11.rt);
  5983. SOKOL_ASSERT(0 == _sapp.d3d11.rtv);
  5984. SOKOL_ASSERT(0 == _sapp.d3d11.msaa_rt);
  5985. SOKOL_ASSERT(0 == _sapp.d3d11.msaa_rtv);
  5986. SOKOL_ASSERT(0 == _sapp.d3d11.ds);
  5987. SOKOL_ASSERT(0 == _sapp.d3d11.dsv);
  5988. HRESULT hr;
  5989. /* view for the swapchain-created framebuffer */
  5990. #ifdef __cplusplus
  5991. hr = _sapp_dxgi_GetBuffer(_sapp.d3d11.swap_chain, 0, IID_ID3D11Texture2D, (void**)&_sapp.d3d11.rt);
  5992. #else
  5993. hr = _sapp_dxgi_GetBuffer(_sapp.d3d11.swap_chain, 0, &IID_ID3D11Texture2D, (void**)&_sapp.d3d11.rt);
  5994. #endif
  5995. SOKOL_ASSERT(SUCCEEDED(hr) && _sapp.d3d11.rt);
  5996. hr = _sapp_d3d11_CreateRenderTargetView(_sapp.d3d11.device, (ID3D11Resource*)_sapp.d3d11.rt, NULL, &_sapp.d3d11.rtv);
  5997. SOKOL_ASSERT(SUCCEEDED(hr) && _sapp.d3d11.rtv);
  5998. /* common desc for MSAA and depth-stencil texture */
  5999. D3D11_TEXTURE2D_DESC tex_desc;
  6000. memset(&tex_desc, 0, sizeof(tex_desc));
  6001. tex_desc.Width = (UINT)_sapp.framebuffer_width;
  6002. tex_desc.Height = (UINT)_sapp.framebuffer_height;
  6003. tex_desc.MipLevels = 1;
  6004. tex_desc.ArraySize = 1;
  6005. tex_desc.Usage = D3D11_USAGE_DEFAULT;
  6006. tex_desc.BindFlags = D3D11_BIND_RENDER_TARGET;
  6007. tex_desc.SampleDesc.Count = (UINT) _sapp.sample_count;
  6008. tex_desc.SampleDesc.Quality = (UINT) (_sapp.sample_count > 1 ? D3D11_STANDARD_MULTISAMPLE_PATTERN : 0);
  6009. /* create MSAA texture and view if antialiasing requested */
  6010. if (_sapp.sample_count > 1) {
  6011. tex_desc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
  6012. hr = _sapp_d3d11_CreateTexture2D(_sapp.d3d11.device, &tex_desc, NULL, &_sapp.d3d11.msaa_rt);
  6013. SOKOL_ASSERT(SUCCEEDED(hr) && _sapp.d3d11.msaa_rt);
  6014. hr = _sapp_d3d11_CreateRenderTargetView(_sapp.d3d11.device, (ID3D11Resource*)_sapp.d3d11.msaa_rt, NULL, &_sapp.d3d11.msaa_rtv);
  6015. SOKOL_ASSERT(SUCCEEDED(hr) && _sapp.d3d11.msaa_rtv);
  6016. }
  6017. /* texture and view for the depth-stencil-surface */
  6018. tex_desc.Format = DXGI_FORMAT_D24_UNORM_S8_UINT;
  6019. tex_desc.BindFlags = D3D11_BIND_DEPTH_STENCIL;
  6020. hr = _sapp_d3d11_CreateTexture2D(_sapp.d3d11.device, &tex_desc, NULL, &_sapp.d3d11.ds);
  6021. SOKOL_ASSERT(SUCCEEDED(hr) && _sapp.d3d11.ds);
  6022. hr = _sapp_d3d11_CreateDepthStencilView(_sapp.d3d11.device, (ID3D11Resource*)_sapp.d3d11.ds, NULL, &_sapp.d3d11.dsv);
  6023. SOKOL_ASSERT(SUCCEEDED(hr) && _sapp.d3d11.dsv);
  6024. }
  6025. _SOKOL_PRIVATE void _sapp_d3d11_destroy_default_render_target(void) {
  6026. _SAPP_SAFE_RELEASE(_sapp.d3d11.rt);
  6027. _SAPP_SAFE_RELEASE(_sapp.d3d11.rtv);
  6028. _SAPP_SAFE_RELEASE(_sapp.d3d11.msaa_rt);
  6029. _SAPP_SAFE_RELEASE(_sapp.d3d11.msaa_rtv);
  6030. _SAPP_SAFE_RELEASE(_sapp.d3d11.ds);
  6031. _SAPP_SAFE_RELEASE(_sapp.d3d11.dsv);
  6032. }
  6033. _SOKOL_PRIVATE void _sapp_d3d11_resize_default_render_target(void) {
  6034. if (_sapp.d3d11.swap_chain) {
  6035. _sapp_d3d11_destroy_default_render_target();
  6036. _sapp_dxgi_ResizeBuffers(_sapp.d3d11.swap_chain, _sapp.d3d11.swap_chain_desc.BufferCount, (UINT)_sapp.framebuffer_width, (UINT)_sapp.framebuffer_height, DXGI_FORMAT_B8G8R8A8_UNORM, 0);
  6037. _sapp_d3d11_create_default_render_target();
  6038. }
  6039. }
  6040. _SOKOL_PRIVATE void _sapp_d3d11_present(void) {
  6041. /* do MSAA resolve if needed */
  6042. if (_sapp.sample_count > 1) {
  6043. SOKOL_ASSERT(_sapp.d3d11.rt);
  6044. SOKOL_ASSERT(_sapp.d3d11.msaa_rt);
  6045. _sapp_d3d11_ResolveSubresource(_sapp.d3d11.device_context, (ID3D11Resource*)_sapp.d3d11.rt, 0, (ID3D11Resource*)_sapp.d3d11.msaa_rt, 0, DXGI_FORMAT_B8G8R8A8_UNORM);
  6046. }
  6047. _sapp_dxgi_Present(_sapp.d3d11.swap_chain, (UINT)_sapp.swap_interval, 0);
  6048. }
  6049. #endif /* SOKOL_D3D11 */
  6050. #if defined(SOKOL_GLCORE33)
  6051. _SOKOL_PRIVATE void _sapp_wgl_init(void) {
  6052. _sapp.wgl.opengl32 = LoadLibraryA("opengl32.dll");
  6053. if (!_sapp.wgl.opengl32) {
  6054. _sapp_fail("Failed to load opengl32.dll\n");
  6055. }
  6056. SOKOL_ASSERT(_sapp.wgl.opengl32);
  6057. _sapp.wgl.CreateContext = (PFN_wglCreateContext)(void*) GetProcAddress(_sapp.wgl.opengl32, "wglCreateContext");
  6058. SOKOL_ASSERT(_sapp.wgl.CreateContext);
  6059. _sapp.wgl.DeleteContext = (PFN_wglDeleteContext)(void*) GetProcAddress(_sapp.wgl.opengl32, "wglDeleteContext");
  6060. SOKOL_ASSERT(_sapp.wgl.DeleteContext);
  6061. _sapp.wgl.GetProcAddress = (PFN_wglGetProcAddress)(void*) GetProcAddress(_sapp.wgl.opengl32, "wglGetProcAddress");
  6062. SOKOL_ASSERT(_sapp.wgl.GetProcAddress);
  6063. _sapp.wgl.GetCurrentDC = (PFN_wglGetCurrentDC)(void*) GetProcAddress(_sapp.wgl.opengl32, "wglGetCurrentDC");
  6064. SOKOL_ASSERT(_sapp.wgl.GetCurrentDC);
  6065. _sapp.wgl.MakeCurrent = (PFN_wglMakeCurrent)(void*) GetProcAddress(_sapp.wgl.opengl32, "wglMakeCurrent");
  6066. SOKOL_ASSERT(_sapp.wgl.MakeCurrent);
  6067. _sapp.wgl.msg_hwnd = CreateWindowExW(WS_EX_OVERLAPPEDWINDOW,
  6068. L"SOKOLAPP",
  6069. L"sokol-app message window",
  6070. WS_CLIPSIBLINGS|WS_CLIPCHILDREN,
  6071. 0, 0, 1, 1,
  6072. NULL, NULL,
  6073. GetModuleHandleW(NULL),
  6074. NULL);
  6075. if (!_sapp.wgl.msg_hwnd) {
  6076. _sapp_fail("Win32: failed to create helper window!\n");
  6077. }
  6078. SOKOL_ASSERT(_sapp.wgl.msg_hwnd);
  6079. ShowWindow(_sapp.wgl.msg_hwnd, SW_HIDE);
  6080. MSG msg;
  6081. while (PeekMessageW(&msg, _sapp.wgl.msg_hwnd, 0, 0, PM_REMOVE)) {
  6082. TranslateMessage(&msg);
  6083. DispatchMessageW(&msg);
  6084. }
  6085. _sapp.wgl.msg_dc = GetDC(_sapp.wgl.msg_hwnd);
  6086. if (!_sapp.wgl.msg_dc) {
  6087. _sapp_fail("Win32: failed to obtain helper window DC!\n");
  6088. }
  6089. }
  6090. _SOKOL_PRIVATE void _sapp_wgl_shutdown(void) {
  6091. SOKOL_ASSERT(_sapp.wgl.opengl32 && _sapp.wgl.msg_hwnd);
  6092. DestroyWindow(_sapp.wgl.msg_hwnd); _sapp.wgl.msg_hwnd = 0;
  6093. FreeLibrary(_sapp.wgl.opengl32); _sapp.wgl.opengl32 = 0;
  6094. }
  6095. _SOKOL_PRIVATE bool _sapp_wgl_has_ext(const char* ext, const char* extensions) {
  6096. SOKOL_ASSERT(ext && extensions);
  6097. const char* start = extensions;
  6098. while (true) {
  6099. const char* where = strstr(start, ext);
  6100. if (!where) {
  6101. return false;
  6102. }
  6103. const char* terminator = where + strlen(ext);
  6104. if ((where == start) || (*(where - 1) == ' ')) {
  6105. if (*terminator == ' ' || *terminator == '\0') {
  6106. break;
  6107. }
  6108. }
  6109. start = terminator;
  6110. }
  6111. return true;
  6112. }
  6113. _SOKOL_PRIVATE bool _sapp_wgl_ext_supported(const char* ext) {
  6114. SOKOL_ASSERT(ext);
  6115. if (_sapp.wgl.GetExtensionsStringEXT) {
  6116. const char* extensions = _sapp.wgl.GetExtensionsStringEXT();
  6117. if (extensions) {
  6118. if (_sapp_wgl_has_ext(ext, extensions)) {
  6119. return true;
  6120. }
  6121. }
  6122. }
  6123. if (_sapp.wgl.GetExtensionsStringARB) {
  6124. const char* extensions = _sapp.wgl.GetExtensionsStringARB(_sapp.wgl.GetCurrentDC());
  6125. if (extensions) {
  6126. if (_sapp_wgl_has_ext(ext, extensions)) {
  6127. return true;
  6128. }
  6129. }
  6130. }
  6131. return false;
  6132. }
  6133. _SOKOL_PRIVATE void _sapp_wgl_load_extensions(void) {
  6134. SOKOL_ASSERT(_sapp.wgl.msg_dc);
  6135. PIXELFORMATDESCRIPTOR pfd;
  6136. memset(&pfd, 0, sizeof(pfd));
  6137. pfd.nSize = sizeof(pfd);
  6138. pfd.nVersion = 1;
  6139. pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER;
  6140. pfd.iPixelType = PFD_TYPE_RGBA;
  6141. pfd.cColorBits = 24;
  6142. if (!SetPixelFormat(_sapp.wgl.msg_dc, ChoosePixelFormat(_sapp.wgl.msg_dc, &pfd), &pfd)) {
  6143. _sapp_fail("WGL: failed to set pixel format for dummy context\n");
  6144. }
  6145. HGLRC rc = _sapp.wgl.CreateContext(_sapp.wgl.msg_dc);
  6146. if (!rc) {
  6147. _sapp_fail("WGL: Failed to create dummy context\n");
  6148. }
  6149. if (!_sapp.wgl.MakeCurrent(_sapp.wgl.msg_dc, rc)) {
  6150. _sapp_fail("WGL: Failed to make context current\n");
  6151. }
  6152. _sapp.wgl.GetExtensionsStringEXT = (PFNWGLGETEXTENSIONSSTRINGEXTPROC)(void*) _sapp.wgl.GetProcAddress("wglGetExtensionsStringEXT");
  6153. _sapp.wgl.GetExtensionsStringARB = (PFNWGLGETEXTENSIONSSTRINGARBPROC)(void*) _sapp.wgl.GetProcAddress("wglGetExtensionsStringARB");
  6154. _sapp.wgl.CreateContextAttribsARB = (PFNWGLCREATECONTEXTATTRIBSARBPROC)(void*) _sapp.wgl.GetProcAddress("wglCreateContextAttribsARB");
  6155. _sapp.wgl.SwapIntervalEXT = (PFNWGLSWAPINTERVALEXTPROC)(void*) _sapp.wgl.GetProcAddress("wglSwapIntervalEXT");
  6156. _sapp.wgl.GetPixelFormatAttribivARB = (PFNWGLGETPIXELFORMATATTRIBIVARBPROC)(void*) _sapp.wgl.GetProcAddress("wglGetPixelFormatAttribivARB");
  6157. _sapp.wgl.arb_multisample = _sapp_wgl_ext_supported("WGL_ARB_multisample");
  6158. _sapp.wgl.arb_create_context = _sapp_wgl_ext_supported("WGL_ARB_create_context");
  6159. _sapp.wgl.arb_create_context_profile = _sapp_wgl_ext_supported("WGL_ARB_create_context_profile");
  6160. _sapp.wgl.ext_swap_control = _sapp_wgl_ext_supported("WGL_EXT_swap_control");
  6161. _sapp.wgl.arb_pixel_format = _sapp_wgl_ext_supported("WGL_ARB_pixel_format");
  6162. _sapp.wgl.MakeCurrent(_sapp.wgl.msg_dc, 0);
  6163. _sapp.wgl.DeleteContext(rc);
  6164. }
  6165. _SOKOL_PRIVATE int _sapp_wgl_attrib(int pixel_format, int attrib) {
  6166. SOKOL_ASSERT(_sapp.wgl.arb_pixel_format);
  6167. int value = 0;
  6168. if (!_sapp.wgl.GetPixelFormatAttribivARB(_sapp.win32.dc, pixel_format, 0, 1, &attrib, &value)) {
  6169. _sapp_fail("WGL: Failed to retrieve pixel format attribute\n");
  6170. }
  6171. return value;
  6172. }
  6173. _SOKOL_PRIVATE int _sapp_wgl_find_pixel_format(void) {
  6174. SOKOL_ASSERT(_sapp.win32.dc);
  6175. SOKOL_ASSERT(_sapp.wgl.arb_pixel_format);
  6176. const _sapp_gl_fbconfig* closest;
  6177. int native_count = _sapp_wgl_attrib(1, WGL_NUMBER_PIXEL_FORMATS_ARB);
  6178. _sapp_gl_fbconfig* usable_configs = (_sapp_gl_fbconfig*) SOKOL_CALLOC((size_t)native_count, sizeof(_sapp_gl_fbconfig));
  6179. SOKOL_ASSERT(usable_configs);
  6180. int usable_count = 0;
  6181. for (int i = 0; i < native_count; i++) {
  6182. const int n = i + 1;
  6183. _sapp_gl_fbconfig* u = usable_configs + usable_count;
  6184. _sapp_gl_init_fbconfig(u);
  6185. if (!_sapp_wgl_attrib(n, WGL_SUPPORT_OPENGL_ARB) || !_sapp_wgl_attrib(n, WGL_DRAW_TO_WINDOW_ARB)) {
  6186. continue;
  6187. }
  6188. if (_sapp_wgl_attrib(n, WGL_PIXEL_TYPE_ARB) != WGL_TYPE_RGBA_ARB) {
  6189. continue;
  6190. }
  6191. if (_sapp_wgl_attrib(n, WGL_ACCELERATION_ARB) == WGL_NO_ACCELERATION_ARB) {
  6192. continue;
  6193. }
  6194. u->red_bits = _sapp_wgl_attrib(n, WGL_RED_BITS_ARB);
  6195. u->green_bits = _sapp_wgl_attrib(n, WGL_GREEN_BITS_ARB);
  6196. u->blue_bits = _sapp_wgl_attrib(n, WGL_BLUE_BITS_ARB);
  6197. u->alpha_bits = _sapp_wgl_attrib(n, WGL_ALPHA_BITS_ARB);
  6198. u->depth_bits = _sapp_wgl_attrib(n, WGL_DEPTH_BITS_ARB);
  6199. u->stencil_bits = _sapp_wgl_attrib(n, WGL_STENCIL_BITS_ARB);
  6200. if (_sapp_wgl_attrib(n, WGL_DOUBLE_BUFFER_ARB)) {
  6201. u->doublebuffer = true;
  6202. }
  6203. if (_sapp.wgl.arb_multisample) {
  6204. u->samples = _sapp_wgl_attrib(n, WGL_SAMPLES_ARB);
  6205. }
  6206. u->handle = (uintptr_t)n;
  6207. usable_count++;
  6208. }
  6209. SOKOL_ASSERT(usable_count > 0);
  6210. _sapp_gl_fbconfig desired;
  6211. _sapp_gl_init_fbconfig(&desired);
  6212. desired.red_bits = 8;
  6213. desired.green_bits = 8;
  6214. desired.blue_bits = 8;
  6215. desired.alpha_bits = 8;
  6216. desired.depth_bits = 24;
  6217. desired.stencil_bits = 8;
  6218. desired.doublebuffer = true;
  6219. desired.samples = _sapp.sample_count > 1 ? _sapp.sample_count : 0;
  6220. closest = _sapp_gl_choose_fbconfig(&desired, usable_configs, usable_count);
  6221. int pixel_format = 0;
  6222. if (closest) {
  6223. pixel_format = (int) closest->handle;
  6224. }
  6225. SOKOL_FREE(usable_configs);
  6226. return pixel_format;
  6227. }
  6228. _SOKOL_PRIVATE void _sapp_wgl_create_context(void) {
  6229. int pixel_format = _sapp_wgl_find_pixel_format();
  6230. if (0 == pixel_format) {
  6231. _sapp_fail("WGL: Didn't find matching pixel format.\n");
  6232. }
  6233. PIXELFORMATDESCRIPTOR pfd;
  6234. if (!DescribePixelFormat(_sapp.win32.dc, pixel_format, sizeof(pfd), &pfd)) {
  6235. _sapp_fail("WGL: Failed to retrieve PFD for selected pixel format!\n");
  6236. }
  6237. if (!SetPixelFormat(_sapp.win32.dc, pixel_format, &pfd)) {
  6238. _sapp_fail("WGL: Failed to set selected pixel format!\n");
  6239. }
  6240. if (!_sapp.wgl.arb_create_context) {
  6241. _sapp_fail("WGL: ARB_create_context required!\n");
  6242. }
  6243. if (!_sapp.wgl.arb_create_context_profile) {
  6244. _sapp_fail("WGL: ARB_create_context_profile required!\n");
  6245. }
  6246. const int attrs[] = {
  6247. WGL_CONTEXT_MAJOR_VERSION_ARB, 3,
  6248. WGL_CONTEXT_MINOR_VERSION_ARB, 3,
  6249. WGL_CONTEXT_FLAGS_ARB, WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB,
  6250. WGL_CONTEXT_PROFILE_MASK_ARB, WGL_CONTEXT_CORE_PROFILE_BIT_ARB,
  6251. 0, 0
  6252. };
  6253. _sapp.wgl.gl_ctx = _sapp.wgl.CreateContextAttribsARB(_sapp.win32.dc, 0, attrs);
  6254. if (!_sapp.wgl.gl_ctx) {
  6255. const DWORD err = GetLastError();
  6256. if (err == (0xc0070000 | ERROR_INVALID_VERSION_ARB)) {
  6257. _sapp_fail("WGL: Driver does not support OpenGL version 3.3\n");
  6258. }
  6259. else if (err == (0xc0070000 | ERROR_INVALID_PROFILE_ARB)) {
  6260. _sapp_fail("WGL: Driver does not support the requested OpenGL profile");
  6261. }
  6262. else if (err == (0xc0070000 | ERROR_INCOMPATIBLE_DEVICE_CONTEXTS_ARB)) {
  6263. _sapp_fail("WGL: The share context is not compatible with the requested context");
  6264. }
  6265. else {
  6266. _sapp_fail("WGL: Failed to create OpenGL context");
  6267. }
  6268. }
  6269. _sapp.wgl.MakeCurrent(_sapp.win32.dc, _sapp.wgl.gl_ctx);
  6270. if (_sapp.wgl.ext_swap_control) {
  6271. /* FIXME: DwmIsCompositionEnabled() (see GLFW) */
  6272. _sapp.wgl.SwapIntervalEXT(_sapp.swap_interval);
  6273. }
  6274. }
  6275. _SOKOL_PRIVATE void _sapp_wgl_destroy_context(void) {
  6276. SOKOL_ASSERT(_sapp.wgl.gl_ctx);
  6277. _sapp.wgl.DeleteContext(_sapp.wgl.gl_ctx);
  6278. _sapp.wgl.gl_ctx = 0;
  6279. }
  6280. _SOKOL_PRIVATE void _sapp_wgl_swap_buffers(void) {
  6281. SOKOL_ASSERT(_sapp.win32.dc);
  6282. /* FIXME: DwmIsCompositionEnabled? (see GLFW) */
  6283. SwapBuffers(_sapp.win32.dc);
  6284. }
  6285. #endif /* SOKOL_GLCORE33 */
  6286. _SOKOL_PRIVATE bool _sapp_win32_wide_to_utf8(const wchar_t* src, char* dst, int dst_num_bytes) {
  6287. SOKOL_ASSERT(src && dst && (dst_num_bytes > 1));
  6288. memset(dst, 0, (size_t)dst_num_bytes);
  6289. const int bytes_needed = WideCharToMultiByte(CP_UTF8, 0, src, -1, NULL, 0, NULL, NULL);
  6290. if (bytes_needed <= dst_num_bytes) {
  6291. WideCharToMultiByte(CP_UTF8, 0, src, -1, dst, dst_num_bytes, NULL, NULL);
  6292. return true;
  6293. }
  6294. else {
  6295. return false;
  6296. }
  6297. }
  6298. _SOKOL_PRIVATE void _sapp_win32_toggle_fullscreen(void) {
  6299. HMONITOR monitor = MonitorFromWindow(_sapp.win32.hwnd, MONITOR_DEFAULTTONEAREST);
  6300. MONITORINFO minfo;
  6301. memset(&minfo, 0, sizeof(minfo));
  6302. minfo.cbSize = sizeof(MONITORINFO);
  6303. GetMonitorInfo(monitor, &minfo);
  6304. const RECT mr = minfo.rcMonitor;
  6305. const int monitor_w = mr.right - mr.left;
  6306. const int monitor_h = mr.bottom - mr.top;
  6307. const DWORD win_ex_style = WS_EX_APPWINDOW | WS_EX_WINDOWEDGE;
  6308. DWORD win_style;
  6309. RECT rect = { 0, 0, 0, 0 };
  6310. _sapp.fullscreen = !_sapp.fullscreen;
  6311. if (!_sapp.fullscreen) {
  6312. win_style = WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_SIZEBOX;
  6313. rect.right = (int) ((float)_sapp.desc.width * _sapp.win32.dpi.window_scale);
  6314. rect.bottom = (int) ((float)_sapp.desc.height * _sapp.win32.dpi.window_scale);
  6315. }
  6316. else {
  6317. win_style = WS_POPUP | WS_SYSMENU | WS_VISIBLE;
  6318. rect.right = monitor_w;
  6319. rect.bottom = monitor_h;
  6320. }
  6321. AdjustWindowRectEx(&rect, win_style, FALSE, win_ex_style);
  6322. int win_width = rect.right - rect.left;
  6323. int win_height = rect.bottom - rect.top;
  6324. if (!_sapp.fullscreen) {
  6325. rect.left = (monitor_w - win_width) / 2;
  6326. rect.top = (monitor_h - win_height) / 2;
  6327. }
  6328. SetWindowLongPtr(_sapp.win32.hwnd, GWL_STYLE, win_style);
  6329. SetWindowPos(_sapp.win32.hwnd, HWND_TOP, mr.left + rect.left, mr.top + rect.top, win_width, win_height, SWP_SHOWWINDOW | SWP_FRAMECHANGED);
  6330. }
  6331. _SOKOL_PRIVATE void _sapp_win32_show_mouse(bool visible) {
  6332. /* NOTE: this function is only called when the mouse visibility actually changes */
  6333. ShowCursor((BOOL)visible);
  6334. }
  6335. _SOKOL_PRIVATE void _sapp_win32_capture_mouse(uint8_t btn_mask) {
  6336. if (0 == _sapp.win32.mouse_capture_mask) {
  6337. SetCapture(_sapp.win32.hwnd);
  6338. }
  6339. _sapp.win32.mouse_capture_mask |= btn_mask;
  6340. }
  6341. _SOKOL_PRIVATE void _sapp_win32_release_mouse(uint8_t btn_mask) {
  6342. if (0 != _sapp.win32.mouse_capture_mask) {
  6343. _sapp.win32.mouse_capture_mask &= ~btn_mask;
  6344. if (0 == _sapp.win32.mouse_capture_mask) {
  6345. ReleaseCapture();
  6346. }
  6347. }
  6348. }
  6349. _SOKOL_PRIVATE void _sapp_win32_lock_mouse(bool lock) {
  6350. if (lock == _sapp.mouse.locked) {
  6351. return;
  6352. }
  6353. _sapp.mouse.dx = 0.0f;
  6354. _sapp.mouse.dy = 0.0f;
  6355. _sapp.mouse.locked = lock;
  6356. _sapp_win32_release_mouse(0xFF);
  6357. if (_sapp.mouse.locked) {
  6358. /* store the current mouse position, so it can be restored when unlocked */
  6359. POINT pos;
  6360. BOOL res = GetCursorPos(&pos);
  6361. SOKOL_ASSERT(res); _SOKOL_UNUSED(res);
  6362. _sapp.win32.mouse_locked_x = pos.x;
  6363. _sapp.win32.mouse_locked_y = pos.y;
  6364. /* while the mouse is locked, make the mouse cursor invisible and
  6365. confine the mouse movement to a small rectangle inside our window
  6366. (so that we dont miss any mouse up events)
  6367. */
  6368. RECT client_rect = {
  6369. _sapp.win32.mouse_locked_x,
  6370. _sapp.win32.mouse_locked_y,
  6371. _sapp.win32.mouse_locked_x,
  6372. _sapp.win32.mouse_locked_y
  6373. };
  6374. ClipCursor(&client_rect);
  6375. /* make the mouse cursor invisible, this will stack with sapp_show_mouse() */
  6376. ShowCursor(FALSE);
  6377. /* enable raw input for mouse, starts sending WM_INPUT messages to WinProc (see GLFW) */
  6378. const RAWINPUTDEVICE rid = {
  6379. 0x01, // usUsagePage: HID_USAGE_PAGE_GENERIC
  6380. 0x02, // usUsage: HID_USAGE_GENERIC_MOUSE
  6381. 0, // dwFlags
  6382. _sapp.win32.hwnd // hwndTarget
  6383. };
  6384. if (!RegisterRawInputDevices(&rid, 1, sizeof(rid))) {
  6385. SOKOL_LOG("RegisterRawInputDevices() failed (on mouse lock).\n");
  6386. }
  6387. /* in case the raw mouse device only supports absolute position reporting,
  6388. we need to skip the dx/dy compution for the first WM_INPUT event
  6389. */
  6390. _sapp.win32.raw_input_mousepos_valid = false;
  6391. }
  6392. else {
  6393. /* disable raw input for mouse */
  6394. const RAWINPUTDEVICE rid = { 0x01, 0x02, RIDEV_REMOVE, NULL };
  6395. if (!RegisterRawInputDevices(&rid, 1, sizeof(rid))) {
  6396. SOKOL_LOG("RegisterRawInputDevices() failed (on mouse unlock).\n");
  6397. }
  6398. /* let the mouse roam freely again */
  6399. ClipCursor(NULL);
  6400. ShowCursor(TRUE);
  6401. /* restore the 'pre-locked' mouse position */
  6402. BOOL res = SetCursorPos(_sapp.win32.mouse_locked_x, _sapp.win32.mouse_locked_y);
  6403. SOKOL_ASSERT(res); _SOKOL_UNUSED(res);
  6404. }
  6405. }
  6406. /* updates current window and framebuffer size from the window's client rect, returns true if size has changed */
  6407. _SOKOL_PRIVATE bool _sapp_win32_update_dimensions(void) {
  6408. RECT rect;
  6409. if (GetClientRect(_sapp.win32.hwnd, &rect)) {
  6410. _sapp.window_width = (int)((float)(rect.right - rect.left) / _sapp.win32.dpi.window_scale);
  6411. _sapp.window_height = (int)((float)(rect.bottom - rect.top) / _sapp.win32.dpi.window_scale);
  6412. const int fb_width = (int)((float)_sapp.window_width * _sapp.win32.dpi.content_scale);
  6413. const int fb_height = (int)((float)_sapp.window_height * _sapp.win32.dpi.content_scale);
  6414. if ((fb_width != _sapp.framebuffer_width) || (fb_height != _sapp.framebuffer_height)) {
  6415. _sapp.framebuffer_width = fb_width;
  6416. _sapp.framebuffer_height = fb_height;
  6417. /* prevent a framebuffer size of 0 when window is minimized */
  6418. if (_sapp.framebuffer_width == 0) {
  6419. _sapp.framebuffer_width = 1;
  6420. }
  6421. if (_sapp.framebuffer_height == 0) {
  6422. _sapp.framebuffer_height = 1;
  6423. }
  6424. return true;
  6425. }
  6426. }
  6427. else {
  6428. _sapp.window_width = _sapp.window_height = 1;
  6429. _sapp.framebuffer_width = _sapp.framebuffer_height = 1;
  6430. }
  6431. return false;
  6432. }
  6433. _SOKOL_PRIVATE uint32_t _sapp_win32_mods(void) {
  6434. uint32_t mods = 0;
  6435. if (GetKeyState(VK_SHIFT) & (1<<15)) {
  6436. mods |= SAPP_MODIFIER_SHIFT;
  6437. }
  6438. if (GetKeyState(VK_CONTROL) & (1<<15)) {
  6439. mods |= SAPP_MODIFIER_CTRL;
  6440. }
  6441. if (GetKeyState(VK_MENU) & (1<<15)) {
  6442. mods |= SAPP_MODIFIER_ALT;
  6443. }
  6444. if ((GetKeyState(VK_LWIN) | GetKeyState(VK_RWIN)) & (1<<15)) {
  6445. mods |= SAPP_MODIFIER_SUPER;
  6446. }
  6447. const bool swapped = (TRUE == GetSystemMetrics(SM_SWAPBUTTON));
  6448. if (GetAsyncKeyState(VK_LBUTTON)) {
  6449. mods |= swapped ? SAPP_MODIFIER_RMB : SAPP_MODIFIER_LMB;
  6450. }
  6451. if (GetAsyncKeyState(VK_RBUTTON)) {
  6452. mods |= swapped ? SAPP_MODIFIER_LMB : SAPP_MODIFIER_RMB;
  6453. }
  6454. if (GetAsyncKeyState(VK_MBUTTON)) {
  6455. mods |= SAPP_MODIFIER_MMB;
  6456. }
  6457. return mods;
  6458. }
  6459. _SOKOL_PRIVATE void _sapp_win32_mouse_event(sapp_event_type type, sapp_mousebutton btn) {
  6460. if (_sapp_events_enabled()) {
  6461. _sapp_init_event(type);
  6462. _sapp.event.modifiers = _sapp_win32_mods();
  6463. _sapp.event.mouse_button = btn;
  6464. _sapp_call_event(&_sapp.event);
  6465. }
  6466. }
  6467. _SOKOL_PRIVATE void _sapp_win32_scroll_event(float x, float y) {
  6468. if (_sapp_events_enabled()) {
  6469. _sapp_init_event(SAPP_EVENTTYPE_MOUSE_SCROLL);
  6470. _sapp.event.modifiers = _sapp_win32_mods();
  6471. _sapp.event.scroll_x = -x / 30.0f;
  6472. _sapp.event.scroll_y = y / 30.0f;
  6473. _sapp_call_event(&_sapp.event);
  6474. }
  6475. }
  6476. _SOKOL_PRIVATE void _sapp_win32_key_event(sapp_event_type type, int vk, bool repeat) {
  6477. if (_sapp_events_enabled() && (vk < SAPP_MAX_KEYCODES)) {
  6478. _sapp_init_event(type);
  6479. _sapp.event.modifiers = _sapp_win32_mods();
  6480. _sapp.event.key_code = _sapp.keycodes[vk];
  6481. _sapp.event.key_repeat = repeat;
  6482. _sapp_call_event(&_sapp.event);
  6483. /* check if a CLIPBOARD_PASTED event must be sent too */
  6484. if (_sapp.clipboard.enabled &&
  6485. (type == SAPP_EVENTTYPE_KEY_DOWN) &&
  6486. (_sapp.event.modifiers == SAPP_MODIFIER_CTRL) &&
  6487. (_sapp.event.key_code == SAPP_KEYCODE_V))
  6488. {
  6489. _sapp_init_event(SAPP_EVENTTYPE_CLIPBOARD_PASTED);
  6490. _sapp_call_event(&_sapp.event);
  6491. }
  6492. }
  6493. }
  6494. _SOKOL_PRIVATE void _sapp_win32_char_event(uint32_t c, bool repeat) {
  6495. if (_sapp_events_enabled() && (c >= 32)) {
  6496. _sapp_init_event(SAPP_EVENTTYPE_CHAR);
  6497. _sapp.event.modifiers = _sapp_win32_mods();
  6498. _sapp.event.char_code = c;
  6499. _sapp.event.key_repeat = repeat;
  6500. _sapp_call_event(&_sapp.event);
  6501. }
  6502. }
  6503. _SOKOL_PRIVATE void _sapp_win32_files_dropped(HDROP hdrop) {
  6504. if (!_sapp.drop.enabled) {
  6505. return;
  6506. }
  6507. _sapp_clear_drop_buffer();
  6508. bool drop_failed = false;
  6509. const int count = (int) DragQueryFileW(hdrop, 0xffffffff, NULL, 0);
  6510. _sapp.drop.num_files = (count > _sapp.drop.max_files) ? _sapp.drop.max_files : count;
  6511. for (UINT i = 0; i < (UINT)_sapp.drop.num_files; i++) {
  6512. const UINT num_chars = DragQueryFileW(hdrop, i, NULL, 0) + 1;
  6513. WCHAR* buffer = (WCHAR*) SOKOL_CALLOC(num_chars, sizeof(WCHAR));
  6514. DragQueryFileW(hdrop, i, buffer, num_chars);
  6515. if (!_sapp_win32_wide_to_utf8(buffer, _sapp_dropped_file_path_ptr((int)i), _sapp.drop.max_path_length)) {
  6516. SOKOL_LOG("sokol_app.h: dropped file path too long (sapp_desc.max_dropped_file_path_length)\n");
  6517. drop_failed = true;
  6518. }
  6519. SOKOL_FREE(buffer);
  6520. }
  6521. DragFinish(hdrop);
  6522. if (!drop_failed) {
  6523. if (_sapp_events_enabled()) {
  6524. _sapp_init_event(SAPP_EVENTTYPE_FILES_DROPPED);
  6525. _sapp_call_event(&_sapp.event);
  6526. }
  6527. }
  6528. else {
  6529. _sapp_clear_drop_buffer();
  6530. _sapp.drop.num_files = 0;
  6531. }
  6532. }
  6533. _SOKOL_PRIVATE LRESULT CALLBACK _sapp_win32_wndproc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
  6534. if (!_sapp.win32.in_create_window) {
  6535. switch (uMsg) {
  6536. case WM_CLOSE:
  6537. /* only give user a chance to intervene when sapp_quit() wasn't already called */
  6538. if (!_sapp.quit_ordered) {
  6539. /* if window should be closed and event handling is enabled, give user code
  6540. a change to intervene via sapp_cancel_quit()
  6541. */
  6542. _sapp.quit_requested = true;
  6543. _sapp_win32_uwp_app_event(SAPP_EVENTTYPE_QUIT_REQUESTED);
  6544. /* if user code hasn't intervened, quit the app */
  6545. if (_sapp.quit_requested) {
  6546. _sapp.quit_ordered = true;
  6547. }
  6548. }
  6549. if (_sapp.quit_ordered) {
  6550. PostQuitMessage(0);
  6551. }
  6552. return 0;
  6553. case WM_SYSCOMMAND:
  6554. switch (wParam & 0xFFF0) {
  6555. case SC_SCREENSAVE:
  6556. case SC_MONITORPOWER:
  6557. if (_sapp.fullscreen) {
  6558. /* disable screen saver and blanking in fullscreen mode */
  6559. return 0;
  6560. }
  6561. break;
  6562. case SC_KEYMENU:
  6563. /* user trying to access menu via ALT */
  6564. return 0;
  6565. }
  6566. break;
  6567. case WM_ERASEBKGND:
  6568. return 1;
  6569. case WM_SIZE:
  6570. {
  6571. const bool iconified = wParam == SIZE_MINIMIZED;
  6572. if (iconified != _sapp.win32.iconified) {
  6573. _sapp.win32.iconified = iconified;
  6574. if (iconified) {
  6575. _sapp_win32_uwp_app_event(SAPP_EVENTTYPE_ICONIFIED);
  6576. }
  6577. else {
  6578. _sapp_win32_uwp_app_event(SAPP_EVENTTYPE_RESTORED);
  6579. }
  6580. }
  6581. }
  6582. break;
  6583. case WM_SETCURSOR:
  6584. if (_sapp.desc.user_cursor) {
  6585. if (LOWORD(lParam) == HTCLIENT) {
  6586. _sapp_win32_uwp_app_event(SAPP_EVENTTYPE_UPDATE_CURSOR);
  6587. return 1;
  6588. }
  6589. }
  6590. break;
  6591. case WM_LBUTTONDOWN:
  6592. _sapp_win32_mouse_event(SAPP_EVENTTYPE_MOUSE_DOWN, SAPP_MOUSEBUTTON_LEFT);
  6593. _sapp_win32_capture_mouse(1<<SAPP_MOUSEBUTTON_LEFT);
  6594. break;
  6595. case WM_RBUTTONDOWN:
  6596. _sapp_win32_mouse_event(SAPP_EVENTTYPE_MOUSE_DOWN, SAPP_MOUSEBUTTON_RIGHT);
  6597. _sapp_win32_capture_mouse(1<<SAPP_MOUSEBUTTON_RIGHT);
  6598. break;
  6599. case WM_MBUTTONDOWN:
  6600. _sapp_win32_mouse_event(SAPP_EVENTTYPE_MOUSE_DOWN, SAPP_MOUSEBUTTON_MIDDLE);
  6601. _sapp_win32_capture_mouse(1<<SAPP_MOUSEBUTTON_MIDDLE);
  6602. break;
  6603. case WM_LBUTTONUP:
  6604. _sapp_win32_mouse_event(SAPP_EVENTTYPE_MOUSE_UP, SAPP_MOUSEBUTTON_LEFT);
  6605. _sapp_win32_release_mouse(1<<SAPP_MOUSEBUTTON_LEFT);
  6606. break;
  6607. case WM_RBUTTONUP:
  6608. _sapp_win32_mouse_event(SAPP_EVENTTYPE_MOUSE_UP, SAPP_MOUSEBUTTON_RIGHT);
  6609. _sapp_win32_release_mouse(1<<SAPP_MOUSEBUTTON_RIGHT);
  6610. break;
  6611. case WM_MBUTTONUP:
  6612. _sapp_win32_mouse_event(SAPP_EVENTTYPE_MOUSE_UP, SAPP_MOUSEBUTTON_MIDDLE);
  6613. _sapp_win32_release_mouse(1<<SAPP_MOUSEBUTTON_MIDDLE);
  6614. break;
  6615. case WM_MOUSEMOVE:
  6616. if (!_sapp.mouse.locked) {
  6617. const float new_x = (float)GET_X_LPARAM(lParam) * _sapp.win32.dpi.mouse_scale;
  6618. const float new_y = (float)GET_Y_LPARAM(lParam) * _sapp.win32.dpi.mouse_scale;
  6619. /* don't update dx/dy in the very first event */
  6620. if (_sapp.mouse.pos_valid) {
  6621. _sapp.mouse.dx = new_x - _sapp.mouse.x;
  6622. _sapp.mouse.dy = new_y - _sapp.mouse.y;
  6623. }
  6624. _sapp.mouse.x = new_x;
  6625. _sapp.mouse.y = new_y;
  6626. _sapp.mouse.pos_valid = true;
  6627. if (!_sapp.win32.mouse_tracked) {
  6628. _sapp.win32.mouse_tracked = true;
  6629. TRACKMOUSEEVENT tme;
  6630. memset(&tme, 0, sizeof(tme));
  6631. tme.cbSize = sizeof(tme);
  6632. tme.dwFlags = TME_LEAVE;
  6633. tme.hwndTrack = _sapp.win32.hwnd;
  6634. TrackMouseEvent(&tme);
  6635. _sapp_win32_mouse_event(SAPP_EVENTTYPE_MOUSE_ENTER, SAPP_MOUSEBUTTON_INVALID);
  6636. }
  6637. _sapp_win32_mouse_event(SAPP_EVENTTYPE_MOUSE_MOVE, SAPP_MOUSEBUTTON_INVALID);
  6638. }
  6639. break;
  6640. case WM_INPUT:
  6641. /* raw mouse input during mouse-lock */
  6642. if (_sapp.mouse.locked) {
  6643. HRAWINPUT ri = (HRAWINPUT) lParam;
  6644. UINT size = sizeof(_sapp.win32.raw_input_data);
  6645. // see: https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getrawinputdata
  6646. if ((UINT)-1 == GetRawInputData(ri, RID_INPUT, &_sapp.win32.raw_input_data, &size, sizeof(RAWINPUTHEADER))) {
  6647. SOKOL_LOG("GetRawInputData() failed\n");
  6648. break;
  6649. }
  6650. const RAWINPUT* raw_mouse_data = (const RAWINPUT*) &_sapp.win32.raw_input_data;
  6651. if (raw_mouse_data->data.mouse.usFlags & MOUSE_MOVE_ABSOLUTE) {
  6652. /* mouse only reports absolute position
  6653. NOTE: THIS IS UNTESTED, it's unclear from reading the
  6654. Win32 RawInput docs under which circumstances absolute
  6655. positions are sent.
  6656. */
  6657. if (_sapp.win32.raw_input_mousepos_valid) {
  6658. LONG new_x = raw_mouse_data->data.mouse.lLastX;
  6659. LONG new_y = raw_mouse_data->data.mouse.lLastY;
  6660. _sapp.mouse.dx = (float) (new_x - _sapp.win32.raw_input_mousepos_x);
  6661. _sapp.mouse.dy = (float) (new_y - _sapp.win32.raw_input_mousepos_y);
  6662. _sapp.win32.raw_input_mousepos_x = new_x;
  6663. _sapp.win32.raw_input_mousepos_y = new_y;
  6664. _sapp.win32.raw_input_mousepos_valid = true;
  6665. }
  6666. }
  6667. else {
  6668. /* mouse reports movement delta (this seems to be the common case) */
  6669. _sapp.mouse.dx = (float) raw_mouse_data->data.mouse.lLastX;
  6670. _sapp.mouse.dy = (float) raw_mouse_data->data.mouse.lLastY;
  6671. }
  6672. _sapp_win32_mouse_event(SAPP_EVENTTYPE_MOUSE_MOVE, SAPP_MOUSEBUTTON_INVALID);
  6673. }
  6674. break;
  6675. case WM_MOUSELEAVE:
  6676. if (!_sapp.mouse.locked) {
  6677. _sapp.win32.mouse_tracked = false;
  6678. _sapp_win32_mouse_event(SAPP_EVENTTYPE_MOUSE_LEAVE, SAPP_MOUSEBUTTON_INVALID);
  6679. }
  6680. break;
  6681. case WM_MOUSEWHEEL:
  6682. _sapp_win32_scroll_event(0.0f, (float)((SHORT)HIWORD(wParam)));
  6683. break;
  6684. case WM_MOUSEHWHEEL:
  6685. _sapp_win32_scroll_event((float)((SHORT)HIWORD(wParam)), 0.0f);
  6686. break;
  6687. case WM_CHAR:
  6688. _sapp_win32_char_event((uint32_t)wParam, !!(lParam&0x40000000));
  6689. break;
  6690. case WM_KEYDOWN:
  6691. case WM_SYSKEYDOWN:
  6692. _sapp_win32_key_event(SAPP_EVENTTYPE_KEY_DOWN, (int)(HIWORD(lParam)&0x1FF), !!(lParam&0x40000000));
  6693. break;
  6694. case WM_KEYUP:
  6695. case WM_SYSKEYUP:
  6696. _sapp_win32_key_event(SAPP_EVENTTYPE_KEY_UP, (int)(HIWORD(lParam)&0x1FF), false);
  6697. break;
  6698. case WM_ENTERSIZEMOVE:
  6699. SetTimer(_sapp.win32.hwnd, 1, USER_TIMER_MINIMUM, NULL);
  6700. break;
  6701. case WM_EXITSIZEMOVE:
  6702. KillTimer(_sapp.win32.hwnd, 1);
  6703. break;
  6704. case WM_TIMER:
  6705. _sapp_frame();
  6706. #if defined(SOKOL_D3D11)
  6707. _sapp_d3d11_present();
  6708. #endif
  6709. #if defined(SOKOL_GLCORE33)
  6710. _sapp_wgl_swap_buffers();
  6711. #endif
  6712. /* NOTE: resizing the swap-chain during resize leads to a substantial
  6713. memory spike (hundreds of megabytes for a few seconds).
  6714. if (_sapp_win32_update_dimensions()) {
  6715. #if defined(SOKOL_D3D11)
  6716. _sapp_d3d11_resize_default_render_target();
  6717. #endif
  6718. _sapp_win32_uwp_app_event(SAPP_EVENTTYPE_RESIZED);
  6719. }
  6720. */
  6721. break;
  6722. case WM_DROPFILES:
  6723. _sapp_win32_files_dropped((HDROP)wParam);
  6724. break;
  6725. default:
  6726. break;
  6727. }
  6728. }
  6729. return DefWindowProcW(hWnd, uMsg, wParam, lParam);
  6730. }
  6731. _SOKOL_PRIVATE void _sapp_win32_create_window(void) {
  6732. WNDCLASSW wndclassw;
  6733. memset(&wndclassw, 0, sizeof(wndclassw));
  6734. wndclassw.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
  6735. wndclassw.lpfnWndProc = (WNDPROC) _sapp_win32_wndproc;
  6736. wndclassw.hInstance = GetModuleHandleW(NULL);
  6737. wndclassw.hCursor = LoadCursor(NULL, IDC_ARROW);
  6738. wndclassw.hIcon = LoadIcon(NULL, IDI_WINLOGO);
  6739. wndclassw.lpszClassName = L"SOKOLAPP";
  6740. RegisterClassW(&wndclassw);
  6741. DWORD win_style;
  6742. const DWORD win_ex_style = WS_EX_APPWINDOW | WS_EX_WINDOWEDGE;
  6743. RECT rect = { 0, 0, 0, 0 };
  6744. if (_sapp.fullscreen) {
  6745. win_style = WS_POPUP | WS_SYSMENU | WS_VISIBLE;
  6746. rect.right = GetSystemMetrics(SM_CXSCREEN);
  6747. rect.bottom = GetSystemMetrics(SM_CYSCREEN);
  6748. }
  6749. else {
  6750. win_style = WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_SIZEBOX;
  6751. rect.right = (int) ((float)_sapp.window_width * _sapp.win32.dpi.window_scale);
  6752. rect.bottom = (int) ((float)_sapp.window_height * _sapp.win32.dpi.window_scale);
  6753. }
  6754. AdjustWindowRectEx(&rect, win_style, FALSE, win_ex_style);
  6755. const int win_width = rect.right - rect.left;
  6756. const int win_height = rect.bottom - rect.top;
  6757. _sapp.win32.in_create_window = true;
  6758. _sapp.win32.hwnd = CreateWindowExW(
  6759. win_ex_style, /* dwExStyle */
  6760. L"SOKOLAPP", /* lpClassName */
  6761. _sapp.window_title_wide, /* lpWindowName */
  6762. win_style, /* dwStyle */
  6763. CW_USEDEFAULT, /* X */
  6764. CW_USEDEFAULT, /* Y */
  6765. win_width, /* nWidth */
  6766. win_height, /* nHeight */
  6767. NULL, /* hWndParent */
  6768. NULL, /* hMenu */
  6769. GetModuleHandle(NULL), /* hInstance */
  6770. NULL); /* lParam */
  6771. ShowWindow(_sapp.win32.hwnd, SW_SHOW);
  6772. _sapp.win32.in_create_window = false;
  6773. _sapp.win32.dc = GetDC(_sapp.win32.hwnd);
  6774. SOKOL_ASSERT(_sapp.win32.dc);
  6775. _sapp_win32_update_dimensions();
  6776. DragAcceptFiles(_sapp.win32.hwnd, 1);
  6777. }
  6778. _SOKOL_PRIVATE void _sapp_win32_destroy_window(void) {
  6779. DestroyWindow(_sapp.win32.hwnd); _sapp.win32.hwnd = 0;
  6780. UnregisterClassW(L"SOKOLAPP", GetModuleHandleW(NULL));
  6781. }
  6782. _SOKOL_PRIVATE void _sapp_win32_destroy_icons(void) {
  6783. if (_sapp.win32.big_icon) {
  6784. DestroyIcon(_sapp.win32.big_icon);
  6785. _sapp.win32.big_icon = 0;
  6786. }
  6787. if (_sapp.win32.small_icon) {
  6788. DestroyIcon(_sapp.win32.small_icon);
  6789. _sapp.win32.small_icon = 0;
  6790. }
  6791. }
  6792. _SOKOL_PRIVATE void _sapp_win32_init_console(void) {
  6793. if (_sapp.desc.win32_console_create || _sapp.desc.win32_console_attach) {
  6794. BOOL con_valid = FALSE;
  6795. if (_sapp.desc.win32_console_create) {
  6796. con_valid = AllocConsole();
  6797. }
  6798. else if (_sapp.desc.win32_console_attach) {
  6799. con_valid = AttachConsole(ATTACH_PARENT_PROCESS);
  6800. }
  6801. if (con_valid) {
  6802. FILE* res_fp = 0;
  6803. errno_t err;
  6804. err = freopen_s(&res_fp, "CON", "w", stdout);
  6805. err = freopen_s(&res_fp, "CON", "w", stderr);
  6806. _SOKOL_UNUSED(err);
  6807. }
  6808. }
  6809. if (_sapp.desc.win32_console_utf8) {
  6810. _sapp.win32.orig_codepage = GetConsoleOutputCP();
  6811. SetConsoleOutputCP(CP_UTF8);
  6812. }
  6813. }
  6814. _SOKOL_PRIVATE void _sapp_win32_restore_console(void) {
  6815. if (_sapp.desc.win32_console_utf8) {
  6816. SetConsoleOutputCP(_sapp.win32.orig_codepage);
  6817. }
  6818. }
  6819. _SOKOL_PRIVATE void _sapp_win32_init_dpi(void) {
  6820. typedef BOOL(WINAPI * SETPROCESSDPIAWARE_T)(void);
  6821. typedef HRESULT(WINAPI * SETPROCESSDPIAWARENESS_T)(PROCESS_DPI_AWARENESS);
  6822. typedef HRESULT(WINAPI * GETDPIFORMONITOR_T)(HMONITOR, MONITOR_DPI_TYPE, UINT*, UINT*);
  6823. SETPROCESSDPIAWARE_T fn_setprocessdpiaware = 0;
  6824. SETPROCESSDPIAWARENESS_T fn_setprocessdpiawareness = 0;
  6825. GETDPIFORMONITOR_T fn_getdpiformonitor = 0;
  6826. HINSTANCE user32 = LoadLibraryA("user32.dll");
  6827. if (user32) {
  6828. fn_setprocessdpiaware = (SETPROCESSDPIAWARE_T)(void*) GetProcAddress(user32, "SetProcessDPIAware");
  6829. }
  6830. HINSTANCE shcore = LoadLibraryA("shcore.dll");
  6831. if (shcore) {
  6832. fn_setprocessdpiawareness = (SETPROCESSDPIAWARENESS_T)(void*) GetProcAddress(shcore, "SetProcessDpiAwareness");
  6833. fn_getdpiformonitor = (GETDPIFORMONITOR_T)(void*) GetProcAddress(shcore, "GetDpiForMonitor");
  6834. }
  6835. if (fn_setprocessdpiawareness) {
  6836. /* if the app didn't request HighDPI rendering, let Windows do the upscaling */
  6837. PROCESS_DPI_AWARENESS process_dpi_awareness = PROCESS_SYSTEM_DPI_AWARE;
  6838. _sapp.win32.dpi.aware = true;
  6839. if (!_sapp.desc.high_dpi) {
  6840. process_dpi_awareness = PROCESS_DPI_UNAWARE;
  6841. _sapp.win32.dpi.aware = false;
  6842. }
  6843. fn_setprocessdpiawareness(process_dpi_awareness);
  6844. }
  6845. else if (fn_setprocessdpiaware) {
  6846. fn_setprocessdpiaware();
  6847. _sapp.win32.dpi.aware = true;
  6848. }
  6849. /* get dpi scale factor for main monitor */
  6850. if (fn_getdpiformonitor && _sapp.win32.dpi.aware) {
  6851. POINT pt = { 1, 1 };
  6852. HMONITOR hm = MonitorFromPoint(pt, MONITOR_DEFAULTTONEAREST);
  6853. UINT dpix, dpiy;
  6854. HRESULT hr = fn_getdpiformonitor(hm, MDT_EFFECTIVE_DPI, &dpix, &dpiy);
  6855. _SOKOL_UNUSED(hr);
  6856. SOKOL_ASSERT(SUCCEEDED(hr));
  6857. /* clamp window scale to an integer factor */
  6858. _sapp.win32.dpi.window_scale = (float)dpix / 96.0f;
  6859. }
  6860. else {
  6861. _sapp.win32.dpi.window_scale = 1.0f;
  6862. }
  6863. if (_sapp.desc.high_dpi) {
  6864. _sapp.win32.dpi.content_scale = _sapp.win32.dpi.window_scale;
  6865. _sapp.win32.dpi.mouse_scale = 1.0f;
  6866. }
  6867. else {
  6868. _sapp.win32.dpi.content_scale = 1.0f;
  6869. _sapp.win32.dpi.mouse_scale = 1.0f / _sapp.win32.dpi.window_scale;
  6870. }
  6871. _sapp.dpi_scale = _sapp.win32.dpi.content_scale;
  6872. if (user32) {
  6873. FreeLibrary(user32);
  6874. }
  6875. if (shcore) {
  6876. FreeLibrary(shcore);
  6877. }
  6878. }
  6879. _SOKOL_PRIVATE bool _sapp_win32_set_clipboard_string(const char* str) {
  6880. SOKOL_ASSERT(str);
  6881. SOKOL_ASSERT(_sapp.win32.hwnd);
  6882. SOKOL_ASSERT(_sapp.clipboard.enabled && (_sapp.clipboard.buf_size > 0));
  6883. wchar_t* wchar_buf = 0;
  6884. const SIZE_T wchar_buf_size = (SIZE_T)_sapp.clipboard.buf_size * sizeof(wchar_t);
  6885. HANDLE object = GlobalAlloc(GMEM_MOVEABLE, wchar_buf_size);
  6886. if (!object) {
  6887. goto error;
  6888. }
  6889. wchar_buf = (wchar_t*) GlobalLock(object);
  6890. if (!wchar_buf) {
  6891. goto error;
  6892. }
  6893. if (!_sapp_win32_uwp_utf8_to_wide(str, wchar_buf, (int)wchar_buf_size)) {
  6894. goto error;
  6895. }
  6896. GlobalUnlock(wchar_buf);
  6897. wchar_buf = 0;
  6898. if (!OpenClipboard(_sapp.win32.hwnd)) {
  6899. goto error;
  6900. }
  6901. EmptyClipboard();
  6902. SetClipboardData(CF_UNICODETEXT, object);
  6903. CloseClipboard();
  6904. return true;
  6905. error:
  6906. if (wchar_buf) {
  6907. GlobalUnlock(object);
  6908. }
  6909. if (object) {
  6910. GlobalFree(object);
  6911. }
  6912. return false;
  6913. }
  6914. _SOKOL_PRIVATE const char* _sapp_win32_get_clipboard_string(void) {
  6915. SOKOL_ASSERT(_sapp.clipboard.enabled && _sapp.clipboard.buffer);
  6916. SOKOL_ASSERT(_sapp.win32.hwnd);
  6917. if (!OpenClipboard(_sapp.win32.hwnd)) {
  6918. /* silently ignore any errors and just return the current
  6919. content of the local clipboard buffer
  6920. */
  6921. return _sapp.clipboard.buffer;
  6922. }
  6923. HANDLE object = GetClipboardData(CF_UNICODETEXT);
  6924. if (!object) {
  6925. CloseClipboard();
  6926. return _sapp.clipboard.buffer;
  6927. }
  6928. const wchar_t* wchar_buf = (const wchar_t*) GlobalLock(object);
  6929. if (!wchar_buf) {
  6930. CloseClipboard();
  6931. return _sapp.clipboard.buffer;
  6932. }
  6933. if (!_sapp_win32_wide_to_utf8(wchar_buf, _sapp.clipboard.buffer, _sapp.clipboard.buf_size)) {
  6934. SOKOL_LOG("sokol_app.h: clipboard string didn't fit into clipboard buffer\n");
  6935. }
  6936. GlobalUnlock(object);
  6937. CloseClipboard();
  6938. return _sapp.clipboard.buffer;
  6939. }
  6940. _SOKOL_PRIVATE void _sapp_win32_update_window_title(void) {
  6941. _sapp_win32_uwp_utf8_to_wide(_sapp.window_title, _sapp.window_title_wide, sizeof(_sapp.window_title_wide));
  6942. SetWindowTextW(_sapp.win32.hwnd, _sapp.window_title_wide);
  6943. }
  6944. _SOKOL_PRIVATE HICON _sapp_win32_create_icon_from_image(const sapp_image_desc* desc) {
  6945. BITMAPV5HEADER bi;
  6946. memset(&bi, 0, sizeof(bi));
  6947. bi.bV5Size = sizeof(bi);
  6948. bi.bV5Width = desc->width;
  6949. bi.bV5Height = -desc->height; // NOTE the '-' here to indicate that origin is top-left
  6950. bi.bV5Planes = 1;
  6951. bi.bV5BitCount = 32;
  6952. bi.bV5Compression = BI_BITFIELDS;
  6953. bi.bV5RedMask = 0x00FF0000;
  6954. bi.bV5GreenMask = 0x0000FF00;
  6955. bi.bV5BlueMask = 0x000000FF;
  6956. bi.bV5AlphaMask = 0xFF000000;
  6957. uint8_t* target = 0;
  6958. const uint8_t* source = (const uint8_t*)desc->pixels.ptr;
  6959. HDC dc = GetDC(NULL);
  6960. HBITMAP color = CreateDIBSection(dc, (BITMAPINFO*)&bi, DIB_RGB_COLORS, (void**)&target, NULL, (DWORD)0);
  6961. ReleaseDC(NULL, dc);
  6962. if (0 == color) {
  6963. return NULL;
  6964. }
  6965. SOKOL_ASSERT(target);
  6966. HBITMAP mask = CreateBitmap(desc->width, desc->height, 1, 1, NULL);
  6967. if (0 == mask) {
  6968. DeleteObject(color);
  6969. return NULL;
  6970. }
  6971. for (int i = 0; i < (desc->width*desc->height); i++) {
  6972. target[0] = source[2];
  6973. target[1] = source[1];
  6974. target[2] = source[0];
  6975. target[3] = source[3];
  6976. target += 4;
  6977. source += 4;
  6978. }
  6979. ICONINFO icon_info;
  6980. memset(&icon_info, 0, sizeof(icon_info));
  6981. icon_info.fIcon = true;
  6982. icon_info.xHotspot = 0;
  6983. icon_info.yHotspot = 0;
  6984. icon_info.hbmMask = mask;
  6985. icon_info.hbmColor = color;
  6986. HICON icon_handle = CreateIconIndirect(&icon_info);
  6987. DeleteObject(color);
  6988. DeleteObject(mask);
  6989. return icon_handle;
  6990. }
  6991. _SOKOL_PRIVATE void _sapp_win32_set_icon(const sapp_icon_desc* icon_desc, int num_images) {
  6992. SOKOL_ASSERT((num_images > 0) && (num_images <= SAPP_MAX_ICONIMAGES));
  6993. int big_img_index = _sapp_image_bestmatch(icon_desc->images, num_images, GetSystemMetrics(SM_CXICON), GetSystemMetrics(SM_CYICON));
  6994. int sml_img_index = _sapp_image_bestmatch(icon_desc->images, num_images, GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON));
  6995. HICON big_icon = _sapp_win32_create_icon_from_image(&icon_desc->images[big_img_index]);
  6996. HICON sml_icon = _sapp_win32_create_icon_from_image(&icon_desc->images[sml_img_index]);
  6997. // if icon creation or lookup has failed for some reason, leave the currently set icon untouched
  6998. if (0 != big_icon) {
  6999. SendMessage(_sapp.win32.hwnd, WM_SETICON, ICON_BIG, (LPARAM) big_icon);
  7000. if (0 != _sapp.win32.big_icon) {
  7001. DestroyIcon(_sapp.win32.big_icon);
  7002. }
  7003. _sapp.win32.big_icon = big_icon;
  7004. }
  7005. if (0 != sml_icon) {
  7006. SendMessage(_sapp.win32.hwnd, WM_SETICON, ICON_SMALL, (LPARAM) sml_icon);
  7007. if (0 != _sapp.win32.small_icon) {
  7008. DestroyIcon(_sapp.win32.small_icon);
  7009. }
  7010. _sapp.win32.small_icon = sml_icon;
  7011. }
  7012. }
  7013. /* don't laugh, but this seems to be the easiest and most robust
  7014. way to check if we're running on Win10
  7015. From: https://github.com/videolan/vlc/blob/232fb13b0d6110c4d1b683cde24cf9a7f2c5c2ea/modules/video_output/win32/d3d11_swapchain.c#L263
  7016. */
  7017. _SOKOL_PRIVATE bool _sapp_win32_is_win10_or_greater(void) {
  7018. HMODULE h = GetModuleHandleW(L"kernel32.dll");
  7019. if (NULL != h) {
  7020. return (NULL != GetProcAddress(h, "GetSystemCpuSetInformation"));
  7021. }
  7022. else {
  7023. return false;
  7024. }
  7025. }
  7026. _SOKOL_PRIVATE void _sapp_win32_run(const sapp_desc* desc) {
  7027. _sapp_init_state(desc);
  7028. _sapp_win32_init_console();
  7029. _sapp.win32.is_win10_or_greater = _sapp_win32_is_win10_or_greater();
  7030. _sapp_win32_uwp_init_keytable();
  7031. _sapp_win32_uwp_utf8_to_wide(_sapp.window_title, _sapp.window_title_wide, sizeof(_sapp.window_title_wide));
  7032. _sapp_win32_init_dpi();
  7033. _sapp_win32_create_window();
  7034. _sapp_set_icon(&desc->icon);
  7035. #if defined(SOKOL_D3D11)
  7036. _sapp_d3d11_create_device_and_swapchain();
  7037. _sapp_d3d11_create_default_render_target();
  7038. #endif
  7039. #if defined(SOKOL_GLCORE33)
  7040. _sapp_wgl_init();
  7041. _sapp_wgl_load_extensions();
  7042. _sapp_wgl_create_context();
  7043. #endif
  7044. _sapp.valid = true;
  7045. bool done = false;
  7046. while (!(done || _sapp.quit_ordered)) {
  7047. MSG msg;
  7048. while (PeekMessageW(&msg, NULL, 0, 0, PM_REMOVE)) {
  7049. if (WM_QUIT == msg.message) {
  7050. done = true;
  7051. continue;
  7052. }
  7053. else {
  7054. TranslateMessage(&msg);
  7055. DispatchMessage(&msg);
  7056. }
  7057. }
  7058. _sapp_frame();
  7059. #if defined(SOKOL_D3D11)
  7060. _sapp_d3d11_present();
  7061. if (IsIconic(_sapp.win32.hwnd)) {
  7062. Sleep((DWORD)(16 * _sapp.swap_interval));
  7063. }
  7064. #endif
  7065. #if defined(SOKOL_GLCORE33)
  7066. _sapp_wgl_swap_buffers();
  7067. #endif
  7068. /* check for window resized, this cannot happen in WM_SIZE as it explodes memory usage */
  7069. if (_sapp_win32_update_dimensions()) {
  7070. #if defined(SOKOL_D3D11)
  7071. _sapp_d3d11_resize_default_render_target();
  7072. #endif
  7073. _sapp_win32_uwp_app_event(SAPP_EVENTTYPE_RESIZED);
  7074. }
  7075. if (_sapp.quit_requested) {
  7076. PostMessage(_sapp.win32.hwnd, WM_CLOSE, 0, 0);
  7077. }
  7078. }
  7079. _sapp_call_cleanup();
  7080. #if defined(SOKOL_D3D11)
  7081. _sapp_d3d11_destroy_default_render_target();
  7082. _sapp_d3d11_destroy_device_and_swapchain();
  7083. #else
  7084. _sapp_wgl_destroy_context();
  7085. _sapp_wgl_shutdown();
  7086. #endif
  7087. _sapp_win32_destroy_window();
  7088. _sapp_win32_destroy_icons();
  7089. _sapp_win32_restore_console();
  7090. _sapp_discard_state();
  7091. }
  7092. _SOKOL_PRIVATE char** _sapp_win32_command_line_to_utf8_argv(LPWSTR w_command_line, int* o_argc) {
  7093. int argc = 0;
  7094. char** argv = 0;
  7095. char* args;
  7096. LPWSTR* w_argv = CommandLineToArgvW(w_command_line, &argc);
  7097. if (w_argv == NULL) {
  7098. _sapp_fail("Win32: failed to parse command line");
  7099. } else {
  7100. size_t size = wcslen(w_command_line) * 4;
  7101. argv = (char**) SOKOL_CALLOC(1, ((size_t)argc + 1) * sizeof(char*) + size);
  7102. SOKOL_ASSERT(argv);
  7103. args = (char*) &argv[argc + 1];
  7104. int n;
  7105. for (int i = 0; i < argc; ++i) {
  7106. n = WideCharToMultiByte(CP_UTF8, 0, w_argv[i], -1, args, (int)size, NULL, NULL);
  7107. if (n == 0) {
  7108. _sapp_fail("Win32: failed to convert all arguments to utf8");
  7109. break;
  7110. }
  7111. argv[i] = args;
  7112. size -= (size_t)n;
  7113. args += n;
  7114. }
  7115. LocalFree(w_argv);
  7116. }
  7117. *o_argc = argc;
  7118. return argv;
  7119. }
  7120. #if !defined(SOKOL_NO_ENTRY)
  7121. #if defined(SOKOL_WIN32_FORCE_MAIN)
  7122. int main(int argc, char* argv[]) {
  7123. sapp_desc desc = sokol_main(argc, argv);
  7124. _sapp_win32_run(&desc);
  7125. return 0;
  7126. }
  7127. #else
  7128. int WINAPI WinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPSTR lpCmdLine, _In_ int nCmdShow) {
  7129. _SOKOL_UNUSED(hInstance);
  7130. _SOKOL_UNUSED(hPrevInstance);
  7131. _SOKOL_UNUSED(lpCmdLine);
  7132. _SOKOL_UNUSED(nCmdShow);
  7133. int argc_utf8 = 0;
  7134. char** argv_utf8 = _sapp_win32_command_line_to_utf8_argv(GetCommandLineW(), &argc_utf8);
  7135. sapp_desc desc = sokol_main(argc_utf8, argv_utf8);
  7136. _sapp_win32_run(&desc);
  7137. SOKOL_FREE(argv_utf8);
  7138. return 0;
  7139. }
  7140. #endif /* SOKOL_WIN32_FORCE_MAIN */
  7141. #endif /* SOKOL_NO_ENTRY */
  7142. #ifdef _MSC_VER
  7143. #pragma warning(pop)
  7144. #endif
  7145. #endif /* _SAPP_WIN32 */
  7146. /*== UWP ================================================================*/
  7147. #if defined(_SAPP_UWP)
  7148. // Helper functions
  7149. _SOKOL_PRIVATE void _sapp_uwp_configure_dpi(float monitor_dpi) {
  7150. _sapp.uwp.dpi.window_scale = monitor_dpi / 96.0f;
  7151. if (_sapp.desc.high_dpi) {
  7152. _sapp.uwp.dpi.content_scale = _sapp.uwp.dpi.window_scale;
  7153. _sapp.uwp.dpi.mouse_scale = 1.0f * _sapp.uwp.dpi.window_scale;
  7154. }
  7155. else {
  7156. _sapp.uwp.dpi.content_scale = 1.0f;
  7157. _sapp.uwp.dpi.mouse_scale = 1.0f;
  7158. }
  7159. _sapp.dpi_scale = _sapp.uwp.dpi.content_scale;
  7160. }
  7161. _SOKOL_PRIVATE void _sapp_uwp_show_mouse(bool visible) {
  7162. using namespace winrt::Windows::UI::Core;
  7163. /* NOTE: this function is only called when the mouse visibility actually changes */
  7164. CoreWindow::GetForCurrentThread().PointerCursor(visible ?
  7165. CoreCursor(CoreCursorType::Arrow, 0) :
  7166. CoreCursor(nullptr));
  7167. }
  7168. _SOKOL_PRIVATE uint32_t _sapp_uwp_mods(winrt::Windows::UI::Core::CoreWindow const& sender_window) {
  7169. using namespace winrt::Windows::System;
  7170. using namespace winrt::Windows::UI::Core;
  7171. uint32_t mods = 0;
  7172. if ((sender_window.GetKeyState(VirtualKey::Shift) & CoreVirtualKeyStates::Down) == CoreVirtualKeyStates::Down) {
  7173. mods |= SAPP_MODIFIER_SHIFT;
  7174. }
  7175. if ((sender_window.GetKeyState(VirtualKey::Control) & CoreVirtualKeyStates::Down) == CoreVirtualKeyStates::Down) {
  7176. mods |= SAPP_MODIFIER_CTRL;
  7177. }
  7178. if ((sender_window.GetKeyState(VirtualKey::Menu) & CoreVirtualKeyStates::Down) == CoreVirtualKeyStates::Down) {
  7179. mods |= SAPP_MODIFIER_ALT;
  7180. }
  7181. if (((sender_window.GetKeyState(VirtualKey::LeftWindows) & CoreVirtualKeyStates::Down) == CoreVirtualKeyStates::Down) ||
  7182. ((sender_window.GetKeyState(VirtualKey::RightWindows) & CoreVirtualKeyStates::Down) == CoreVirtualKeyStates::Down))
  7183. {
  7184. mods |= SAPP_MODIFIER_SUPER;
  7185. }
  7186. if (0 != (_sapp.uwp.mouse_buttons & (1<<SAPP_MOUSEBUTTON_LEFT))) {
  7187. mods |= SAPP_MODIFIER_LMB;
  7188. }
  7189. if (0 != (_sapp.uwp.mouse_buttons & (1<<SAPP_MOUSEBUTTON_MIDDLE))) {
  7190. mods |= SAPP_MODIFIER_MMB;
  7191. }
  7192. if (0 != (_sapp.uwp.mouse_buttons & (1<<SAPP_MOUSEBUTTON_RIGHT))) {
  7193. mods |= SAPP_MODIFIER_RMB;
  7194. }
  7195. return mods;
  7196. }
  7197. _SOKOL_PRIVATE void _sapp_uwp_mouse_event(sapp_event_type type, sapp_mousebutton btn, winrt::Windows::UI::Core::CoreWindow const& sender_window) {
  7198. if (_sapp_events_enabled()) {
  7199. _sapp_init_event(type);
  7200. _sapp.event.modifiers = _sapp_uwp_mods(sender_window);
  7201. _sapp.event.mouse_button = btn;
  7202. _sapp_call_event(&_sapp.event);
  7203. }
  7204. }
  7205. _SOKOL_PRIVATE void _sapp_uwp_scroll_event(float delta, bool horizontal, winrt::Windows::UI::Core::CoreWindow const& sender_window) {
  7206. if (_sapp_events_enabled()) {
  7207. _sapp_init_event(SAPP_EVENTTYPE_MOUSE_SCROLL);
  7208. _sapp.event.modifiers = _sapp_uwp_mods(sender_window);
  7209. _sapp.event.scroll_x = horizontal ? (-delta / 30.0f) : 0.0f;
  7210. _sapp.event.scroll_y = horizontal ? 0.0f : (delta / 30.0f);
  7211. _sapp_call_event(&_sapp.event);
  7212. }
  7213. }
  7214. _SOKOL_PRIVATE void _sapp_uwp_extract_mouse_button_events(winrt::Windows::UI::Core::CoreWindow const& sender, winrt::Windows::UI::Core::PointerEventArgs const& args) {
  7215. // we need to figure out ourselves what mouse buttons have been pressed and released,
  7216. // because UWP doesn't properly send down/up mouse button events when multiple buttons
  7217. // are pressed down, so we also need to check the mouse button state in other mouse events
  7218. // to track what buttons have been pressed down and released
  7219. //
  7220. auto properties = args.CurrentPoint().Properties();
  7221. const uint8_t lmb_bit = (1 << SAPP_MOUSEBUTTON_LEFT);
  7222. const uint8_t rmb_bit = (1 << SAPP_MOUSEBUTTON_RIGHT);
  7223. const uint8_t mmb_bit = (1 << SAPP_MOUSEBUTTON_MIDDLE);
  7224. uint8_t new_btns = 0;
  7225. if (properties.IsLeftButtonPressed()) {
  7226. new_btns |= lmb_bit;
  7227. }
  7228. if (properties.IsRightButtonPressed()) {
  7229. new_btns |= rmb_bit;
  7230. }
  7231. if (properties.IsMiddleButtonPressed()) {
  7232. new_btns |= mmb_bit;
  7233. }
  7234. const uint8_t old_btns = _sapp.uwp.mouse_buttons;
  7235. const uint8_t chg_btns = new_btns ^ old_btns;
  7236. _sapp.uwp.mouse_buttons = new_btns;
  7237. sapp_event_type type = SAPP_EVENTTYPE_INVALID;
  7238. sapp_mousebutton btn = SAPP_MOUSEBUTTON_INVALID;
  7239. if (chg_btns & lmb_bit) {
  7240. btn = SAPP_MOUSEBUTTON_LEFT;
  7241. type = (new_btns & lmb_bit) ? SAPP_EVENTTYPE_MOUSE_DOWN : SAPP_EVENTTYPE_MOUSE_UP;
  7242. }
  7243. if (chg_btns & rmb_bit) {
  7244. btn = SAPP_MOUSEBUTTON_RIGHT;
  7245. type = (new_btns & rmb_bit) ? SAPP_EVENTTYPE_MOUSE_DOWN : SAPP_EVENTTYPE_MOUSE_UP;
  7246. }
  7247. if (chg_btns & mmb_bit) {
  7248. btn = SAPP_MOUSEBUTTON_MIDDLE;
  7249. type = (new_btns & mmb_bit) ? SAPP_EVENTTYPE_MOUSE_DOWN : SAPP_EVENTTYPE_MOUSE_UP;
  7250. }
  7251. if (type != SAPP_EVENTTYPE_INVALID) {
  7252. _sapp_uwp_mouse_event(type, btn, sender);
  7253. }
  7254. }
  7255. _SOKOL_PRIVATE void _sapp_uwp_key_event(sapp_event_type type, winrt::Windows::UI::Core::CoreWindow const& sender_window, winrt::Windows::UI::Core::KeyEventArgs const& args) {
  7256. auto key_status = args.KeyStatus();
  7257. uint32_t ext_scan_code = key_status.ScanCode | (key_status.IsExtendedKey ? 0x100 : 0);
  7258. if (_sapp_events_enabled() && (ext_scan_code < SAPP_MAX_KEYCODES)) {
  7259. _sapp_init_event(type);
  7260. _sapp.event.modifiers = _sapp_uwp_mods(sender_window);
  7261. _sapp.event.key_code = _sapp.keycodes[ext_scan_code];
  7262. _sapp.event.key_repeat = type == SAPP_EVENTTYPE_KEY_UP ? false : key_status.WasKeyDown;
  7263. _sapp_call_event(&_sapp.event);
  7264. /* check if a CLIPBOARD_PASTED event must be sent too */
  7265. if (_sapp.clipboard.enabled &&
  7266. (type == SAPP_EVENTTYPE_KEY_DOWN) &&
  7267. (_sapp.event.modifiers == SAPP_MODIFIER_CTRL) &&
  7268. (_sapp.event.key_code == SAPP_KEYCODE_V))
  7269. {
  7270. _sapp_init_event(SAPP_EVENTTYPE_CLIPBOARD_PASTED);
  7271. _sapp_call_event(&_sapp.event);
  7272. }
  7273. }
  7274. }
  7275. _SOKOL_PRIVATE void _sapp_uwp_char_event(uint32_t c, bool repeat, winrt::Windows::UI::Core::CoreWindow const& sender_window) {
  7276. if (_sapp_events_enabled() && (c >= 32)) {
  7277. _sapp_init_event(SAPP_EVENTTYPE_CHAR);
  7278. _sapp.event.modifiers = _sapp_uwp_mods(sender_window);
  7279. _sapp.event.char_code = c;
  7280. _sapp.event.key_repeat = repeat;
  7281. _sapp_call_event(&_sapp.event);
  7282. }
  7283. }
  7284. _SOKOL_PRIVATE void _sapp_uwp_toggle_fullscreen(void) {
  7285. auto appView = winrt::Windows::UI::ViewManagement::ApplicationView::GetForCurrentView();
  7286. _sapp.fullscreen = appView.IsFullScreenMode();
  7287. if (!_sapp.fullscreen) {
  7288. appView.TryEnterFullScreenMode();
  7289. }
  7290. else {
  7291. appView.ExitFullScreenMode();
  7292. }
  7293. _sapp.fullscreen = appView.IsFullScreenMode();
  7294. }
  7295. namespace {/* Empty namespace to ensure internal linkage (same as _SOKOL_PRIVATE) */
  7296. // Controls all the DirectX device resources.
  7297. class DeviceResources {
  7298. public:
  7299. // Provides an interface for an application that owns DeviceResources to be notified of the device being lost or created.
  7300. interface IDeviceNotify {
  7301. virtual void OnDeviceLost() = 0;
  7302. virtual void OnDeviceRestored() = 0;
  7303. };
  7304. DeviceResources();
  7305. ~DeviceResources();
  7306. void SetWindow(winrt::Windows::UI::Core::CoreWindow const& window);
  7307. void SetLogicalSize(winrt::Windows::Foundation::Size logicalSize);
  7308. void SetCurrentOrientation(winrt::Windows::Graphics::Display::DisplayOrientations currentOrientation);
  7309. void SetDpi(float dpi);
  7310. void ValidateDevice();
  7311. void HandleDeviceLost();
  7312. void RegisterDeviceNotify(IDeviceNotify* deviceNotify);
  7313. void Trim();
  7314. void Present();
  7315. private:
  7316. // Swapchain Rotation Matrices (Z-rotation)
  7317. static inline const DirectX::XMFLOAT4X4 DeviceResources::m_rotation0 = {
  7318. 1.0f, 0.0f, 0.0f, 0.0f,
  7319. 0.0f, 1.0f, 0.0f, 0.0f,
  7320. 0.0f, 0.0f, 1.0f, 0.0f,
  7321. 0.0f, 0.0f, 0.0f, 1.0f
  7322. };
  7323. static inline const DirectX::XMFLOAT4X4 DeviceResources::m_rotation90 = {
  7324. 0.0f, 1.0f, 0.0f, 0.0f,
  7325. -1.0f, 0.0f, 0.0f, 0.0f,
  7326. 0.0f, 0.0f, 1.0f, 0.0f,
  7327. 0.0f, 0.0f, 0.0f, 1.0f
  7328. };
  7329. static inline const DirectX::XMFLOAT4X4 DeviceResources::m_rotation180 = {
  7330. -1.0f, 0.0f, 0.0f, 0.0f,
  7331. 0.0f, -1.0f, 0.0f, 0.0f,
  7332. 0.0f, 0.0f, 1.0f, 0.0f,
  7333. 0.0f, 0.0f, 0.0f, 1.0f
  7334. };
  7335. static inline const DirectX::XMFLOAT4X4 DeviceResources::m_rotation270 = {
  7336. 0.0f, -1.0f, 0.0f, 0.0f,
  7337. 1.0f, 0.0f, 0.0f, 0.0f,
  7338. 0.0f, 0.0f, 1.0f, 0.0f,
  7339. 0.0f, 0.0f, 0.0f, 1.0f
  7340. };
  7341. void CreateDeviceResources();
  7342. void CreateWindowSizeDependentResources();
  7343. void UpdateRenderTargetSize();
  7344. DXGI_MODE_ROTATION ComputeDisplayRotation();
  7345. bool SdkLayersAvailable();
  7346. // Direct3D objects.
  7347. winrt::com_ptr<ID3D11Device3> m_d3dDevice;
  7348. winrt::com_ptr<ID3D11DeviceContext3> m_d3dContext;
  7349. winrt::com_ptr<IDXGISwapChain3> m_swapChain;
  7350. // Direct3D rendering objects. Required for 3D.
  7351. winrt::com_ptr<ID3D11Texture2D1> m_d3dRenderTarget;
  7352. winrt::com_ptr<ID3D11RenderTargetView1> m_d3dRenderTargetView;
  7353. winrt::com_ptr<ID3D11Texture2D1> m_d3dMSAARenderTarget;
  7354. winrt::com_ptr<ID3D11RenderTargetView1> m_d3dMSAARenderTargetView;
  7355. winrt::com_ptr<ID3D11Texture2D1> m_d3dDepthStencil;
  7356. winrt::com_ptr<ID3D11DepthStencilView> m_d3dDepthStencilView;
  7357. D3D11_VIEWPORT m_screenViewport = { };
  7358. // Cached reference to the Window.
  7359. winrt::agile_ref< winrt::Windows::UI::Core::CoreWindow> m_window;
  7360. // Cached device properties.
  7361. D3D_FEATURE_LEVEL m_d3dFeatureLevel = D3D_FEATURE_LEVEL_9_1;
  7362. winrt::Windows::Foundation::Size m_d3dRenderTargetSize = { };
  7363. winrt::Windows::Foundation::Size m_outputSize = { };
  7364. winrt::Windows::Foundation::Size m_logicalSize = { };
  7365. winrt::Windows::Graphics::Display::DisplayOrientations m_nativeOrientation = winrt::Windows::Graphics::Display::DisplayOrientations::None;
  7366. winrt::Windows::Graphics::Display::DisplayOrientations m_currentOrientation = winrt::Windows::Graphics::Display::DisplayOrientations::None;
  7367. float m_dpi = -1.0f;
  7368. // Transforms used for display orientation.
  7369. DirectX::XMFLOAT4X4 m_orientationTransform3D;
  7370. // The IDeviceNotify can be held directly as it owns the DeviceResources.
  7371. IDeviceNotify* m_deviceNotify = nullptr;
  7372. };
  7373. // Main entry point for our app. Connects the app with the Windows shell and handles application lifecycle events.
  7374. struct App : winrt::implements<App, winrt::Windows::ApplicationModel::Core::IFrameworkViewSource, winrt::Windows::ApplicationModel::Core::IFrameworkView> {
  7375. public:
  7376. // IFrameworkViewSource Methods
  7377. winrt::Windows::ApplicationModel::Core::IFrameworkView CreateView() { return *this; }
  7378. // IFrameworkView Methods.
  7379. virtual void Initialize(winrt::Windows::ApplicationModel::Core::CoreApplicationView const& applicationView);
  7380. virtual void SetWindow(winrt::Windows::UI::Core::CoreWindow const& window);
  7381. virtual void Load(winrt::hstring const& entryPoint);
  7382. virtual void Run();
  7383. virtual void Uninitialize();
  7384. protected:
  7385. // Application lifecycle event handlers
  7386. void OnActivated(winrt::Windows::ApplicationModel::Core::CoreApplicationView const& applicationView, winrt::Windows::ApplicationModel::Activation::IActivatedEventArgs const& args);
  7387. void OnSuspending(winrt::Windows::Foundation::IInspectable const& sender, winrt::Windows::ApplicationModel::SuspendingEventArgs const& args);
  7388. void OnResuming(winrt::Windows::Foundation::IInspectable const& sender, winrt::Windows::Foundation::IInspectable const& args);
  7389. // Window event handlers
  7390. void OnWindowSizeChanged(winrt::Windows::UI::Core::CoreWindow const& sender, winrt::Windows::UI::Core::WindowSizeChangedEventArgs const& args);
  7391. void OnVisibilityChanged(winrt::Windows::UI::Core::CoreWindow const& sender, winrt::Windows::UI::Core::VisibilityChangedEventArgs const& args);
  7392. // Navigation event handlers
  7393. void OnBackRequested(winrt::Windows::Foundation::IInspectable const& sender, winrt::Windows::UI::Core::BackRequestedEventArgs const& args);
  7394. // Input event handlers
  7395. void OnKeyDown(winrt::Windows::UI::Core::CoreWindow const& sender, winrt::Windows::UI::Core::KeyEventArgs const& args);
  7396. void OnKeyUp(winrt::Windows::UI::Core::CoreWindow const& sender, winrt::Windows::UI::Core::KeyEventArgs const& args);
  7397. void OnCharacterReceived(winrt::Windows::UI::Core::CoreWindow const& sender, winrt::Windows::UI::Core::CharacterReceivedEventArgs const& args);
  7398. // Pointer event handlers
  7399. void OnPointerEntered(winrt::Windows::UI::Core::CoreWindow const& sender, winrt::Windows::UI::Core::PointerEventArgs const& args);
  7400. void OnPointerExited(winrt::Windows::UI::Core::CoreWindow const& sender, winrt::Windows::UI::Core::PointerEventArgs const& args);
  7401. void OnPointerPressed(winrt::Windows::UI::Core::CoreWindow const& sender, winrt::Windows::UI::Core::PointerEventArgs const& args);
  7402. void OnPointerReleased(winrt::Windows::UI::Core::CoreWindow const& sender, winrt::Windows::UI::Core::PointerEventArgs const& args);
  7403. void OnPointerMoved(winrt::Windows::UI::Core::CoreWindow const& sender, winrt::Windows::UI::Core::PointerEventArgs const& args);
  7404. void OnPointerWheelChanged(winrt::Windows::UI::Core::CoreWindow const& sender, winrt::Windows::UI::Core::PointerEventArgs const& args);
  7405. // DisplayInformation event handlers.
  7406. void OnDpiChanged(winrt::Windows::Graphics::Display::DisplayInformation const& sender, winrt::Windows::Foundation::IInspectable const& args);
  7407. void OnOrientationChanged(winrt::Windows::Graphics::Display::DisplayInformation const& sender, winrt::Windows::Foundation::IInspectable const& args);
  7408. void OnDisplayContentsInvalidated(winrt::Windows::Graphics::Display::DisplayInformation const& sender, winrt::Windows::Foundation::IInspectable const& args);
  7409. private:
  7410. std::unique_ptr<DeviceResources> m_deviceResources;
  7411. bool m_windowVisible = true;
  7412. };
  7413. DeviceResources::DeviceResources() {
  7414. CreateDeviceResources();
  7415. }
  7416. DeviceResources::~DeviceResources() {
  7417. // Cleanup Sokol Context
  7418. _sapp.d3d11.device = nullptr;
  7419. _sapp.d3d11.device_context = nullptr;
  7420. }
  7421. void DeviceResources::CreateDeviceResources() {
  7422. // This flag adds support for surfaces with a different color channel ordering
  7423. // than the API default. It is required for compatibility with Direct2D.
  7424. UINT creationFlags = D3D11_CREATE_DEVICE_BGRA_SUPPORT;
  7425. #if defined(_DEBUG)
  7426. if (SdkLayersAvailable()) {
  7427. // If the project is in a debug build, enable debugging via SDK Layers with this flag.
  7428. creationFlags |= D3D11_CREATE_DEVICE_DEBUG;
  7429. }
  7430. #endif
  7431. // This array defines the set of DirectX hardware feature levels this app will support.
  7432. // Note the ordering should be preserved.
  7433. // Don't forget to declare your application's minimum required feature level in its
  7434. // description. All applications are assumed to support 9.1 unless otherwise stated.
  7435. D3D_FEATURE_LEVEL featureLevels[] = {
  7436. D3D_FEATURE_LEVEL_12_1,
  7437. D3D_FEATURE_LEVEL_12_0,
  7438. D3D_FEATURE_LEVEL_11_1,
  7439. D3D_FEATURE_LEVEL_11_0,
  7440. D3D_FEATURE_LEVEL_10_1,
  7441. D3D_FEATURE_LEVEL_10_0,
  7442. D3D_FEATURE_LEVEL_9_3,
  7443. D3D_FEATURE_LEVEL_9_2,
  7444. D3D_FEATURE_LEVEL_9_1
  7445. };
  7446. // Create the Direct3D 11 API device object and a corresponding context.
  7447. winrt::com_ptr<ID3D11Device> device;
  7448. winrt::com_ptr<ID3D11DeviceContext> context;
  7449. HRESULT hr = D3D11CreateDevice(
  7450. nullptr, // Specify nullptr to use the default adapter.
  7451. D3D_DRIVER_TYPE_HARDWARE, // Create a device using the hardware graphics driver.
  7452. 0, // Should be 0 unless the driver is D3D_DRIVER_TYPE_SOFTWARE.
  7453. creationFlags, // Set debug and Direct2D compatibility flags.
  7454. featureLevels, // List of feature levels this app can support.
  7455. ARRAYSIZE(featureLevels), // Size of the list above.
  7456. D3D11_SDK_VERSION, // Always set this to D3D11_SDK_VERSION for Microsoft Store apps.
  7457. device.put(), // Returns the Direct3D device created.
  7458. &m_d3dFeatureLevel, // Returns feature level of device created.
  7459. context.put() // Returns the device immediate context.
  7460. );
  7461. if (FAILED(hr)) {
  7462. // If the initialization fails, fall back to the WARP device.
  7463. // For more information on WARP, see:
  7464. // https://go.microsoft.com/fwlink/?LinkId=286690
  7465. winrt::check_hresult(
  7466. D3D11CreateDevice(
  7467. nullptr,
  7468. D3D_DRIVER_TYPE_WARP, // Create a WARP device instead of a hardware device.
  7469. 0,
  7470. creationFlags,
  7471. featureLevels,
  7472. ARRAYSIZE(featureLevels),
  7473. D3D11_SDK_VERSION,
  7474. device.put(),
  7475. &m_d3dFeatureLevel,
  7476. context.put()
  7477. )
  7478. );
  7479. }
  7480. // Store pointers to the Direct3D 11.3 API device and immediate context.
  7481. m_d3dDevice = device.as<ID3D11Device3>();
  7482. m_d3dContext = context.as<ID3D11DeviceContext3>();
  7483. // Setup Sokol Context
  7484. _sapp.d3d11.device = m_d3dDevice.get();
  7485. _sapp.d3d11.device_context = m_d3dContext.get();
  7486. }
  7487. void DeviceResources::CreateWindowSizeDependentResources() {
  7488. // Cleanup Sokol Context (these are non-owning raw pointers)
  7489. _sapp.d3d11.rt = nullptr;
  7490. _sapp.d3d11.rtv = nullptr;
  7491. _sapp.d3d11.msaa_rt = nullptr;
  7492. _sapp.d3d11.msaa_rtv = nullptr;
  7493. _sapp.d3d11.ds = nullptr;
  7494. _sapp.d3d11.dsv = nullptr;
  7495. // Clear the previous window size specific context.
  7496. ID3D11RenderTargetView* nullViews[] = { nullptr };
  7497. m_d3dContext->OMSetRenderTargets(ARRAYSIZE(nullViews), nullViews, nullptr);
  7498. // these are smart pointers, setting to nullptr will delete the objects
  7499. m_d3dRenderTarget = nullptr;
  7500. m_d3dRenderTargetView = nullptr;
  7501. m_d3dMSAARenderTarget = nullptr;
  7502. m_d3dMSAARenderTargetView = nullptr;
  7503. m_d3dDepthStencilView = nullptr;
  7504. m_d3dDepthStencil = nullptr;
  7505. m_d3dContext->Flush1(D3D11_CONTEXT_TYPE_ALL, nullptr);
  7506. UpdateRenderTargetSize();
  7507. // The width and height of the swap chain must be based on the window's
  7508. // natively-oriented width and height. If the window is not in the native
  7509. // orientation, the dimensions must be reversed.
  7510. DXGI_MODE_ROTATION displayRotation = ComputeDisplayRotation();
  7511. bool swapDimensions = displayRotation == DXGI_MODE_ROTATION_ROTATE90 || displayRotation == DXGI_MODE_ROTATION_ROTATE270;
  7512. m_d3dRenderTargetSize.Width = swapDimensions ? m_outputSize.Height : m_outputSize.Width;
  7513. m_d3dRenderTargetSize.Height = swapDimensions ? m_outputSize.Width : m_outputSize.Height;
  7514. if (m_swapChain != nullptr) {
  7515. // If the swap chain already exists, resize it.
  7516. HRESULT hr = m_swapChain->ResizeBuffers(
  7517. 2, // Double-buffered swap chain.
  7518. lround(m_d3dRenderTargetSize.Width),
  7519. lround(m_d3dRenderTargetSize.Height),
  7520. DXGI_FORMAT_B8G8R8A8_UNORM,
  7521. 0
  7522. );
  7523. if (hr == DXGI_ERROR_DEVICE_REMOVED || hr == DXGI_ERROR_DEVICE_RESET) {
  7524. // If the device was removed for any reason, a new device and swap chain will need to be created.
  7525. HandleDeviceLost();
  7526. // Everything is set up now. Do not continue execution of this method. HandleDeviceLost will reenter this method
  7527. // and correctly set up the new device.
  7528. return;
  7529. }
  7530. else {
  7531. winrt::check_hresult(hr);
  7532. }
  7533. }
  7534. else {
  7535. // Otherwise, create a new one using the same adapter as the existing Direct3D device.
  7536. DXGI_SCALING scaling = (_sapp.uwp.dpi.content_scale == _sapp.uwp.dpi.window_scale) ? DXGI_SCALING_NONE : DXGI_SCALING_STRETCH;
  7537. DXGI_SWAP_CHAIN_DESC1 swapChainDesc = { 0 };
  7538. swapChainDesc.Width = lround(m_d3dRenderTargetSize.Width); // Match the size of the window.
  7539. swapChainDesc.Height = lround(m_d3dRenderTargetSize.Height);
  7540. swapChainDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM; // This is the most common swap chain format.
  7541. swapChainDesc.Stereo = false;
  7542. swapChainDesc.SampleDesc.Count = 1; // Don't use multi-sampling.
  7543. swapChainDesc.SampleDesc.Quality = 0;
  7544. swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
  7545. swapChainDesc.BufferCount = 2; // Use double-buffering to minimize latency.
  7546. swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL; // All Microsoft Store apps must use this SwapEffect.
  7547. swapChainDesc.Flags = 0;
  7548. swapChainDesc.Scaling = scaling;
  7549. swapChainDesc.AlphaMode = DXGI_ALPHA_MODE_IGNORE;
  7550. // This sequence obtains the DXGI factory that was used to create the Direct3D device above.
  7551. winrt::com_ptr<IDXGIDevice3> dxgiDevice = m_d3dDevice.as<IDXGIDevice3>();
  7552. winrt::com_ptr<IDXGIAdapter> dxgiAdapter;
  7553. winrt::check_hresult(dxgiDevice->GetAdapter(dxgiAdapter.put()));
  7554. winrt::com_ptr<IDXGIFactory4> dxgiFactory;
  7555. winrt::check_hresult(dxgiAdapter->GetParent(__uuidof(IDXGIFactory4), dxgiFactory.put_void()));
  7556. winrt::com_ptr<IDXGISwapChain1> swapChain;
  7557. winrt::check_hresult(dxgiFactory->CreateSwapChainForCoreWindow(m_d3dDevice.get(), m_window.get().as<::IUnknown>().get(), &swapChainDesc, nullptr, swapChain.put()));
  7558. m_swapChain = swapChain.as<IDXGISwapChain3>();
  7559. // Ensure that DXGI does not queue more than one frame at a time. This both reduces latency and
  7560. // ensures that the application will only render after each VSync, minimizing power consumption.
  7561. winrt::check_hresult(dxgiDevice->SetMaximumFrameLatency(1));
  7562. // Setup Sokol Context
  7563. winrt::check_hresult(swapChain->GetDesc(&_sapp.d3d11.swap_chain_desc));
  7564. _sapp.d3d11.swap_chain = m_swapChain.as<IDXGISwapChain3>().detach();
  7565. }
  7566. // Set the proper orientation for the swap chain, and generate 2D and
  7567. // 3D matrix transformations for rendering to the rotated swap chain.
  7568. // Note the rotation angle for the 2D and 3D transforms are different.
  7569. // This is due to the difference in coordinate spaces. Additionally,
  7570. // the 3D matrix is specified explicitly to avoid rounding errors.
  7571. switch (displayRotation) {
  7572. case DXGI_MODE_ROTATION_IDENTITY:
  7573. m_orientationTransform3D = m_rotation0;
  7574. break;
  7575. case DXGI_MODE_ROTATION_ROTATE90:
  7576. m_orientationTransform3D = m_rotation270;
  7577. break;
  7578. case DXGI_MODE_ROTATION_ROTATE180:
  7579. m_orientationTransform3D = m_rotation180;
  7580. break;
  7581. case DXGI_MODE_ROTATION_ROTATE270:
  7582. m_orientationTransform3D = m_rotation90;
  7583. break;
  7584. }
  7585. winrt::check_hresult(m_swapChain->SetRotation(displayRotation));
  7586. // Create a render target view of the swap chain back buffer.
  7587. winrt::check_hresult(m_swapChain->GetBuffer(0, IID_PPV_ARGS(&m_d3dRenderTarget)));
  7588. winrt::check_hresult(m_d3dDevice->CreateRenderTargetView1(m_d3dRenderTarget.get(), nullptr, m_d3dRenderTargetView.put()));
  7589. // Create MSAA texture and view if needed
  7590. if (_sapp.sample_count > 1) {
  7591. CD3D11_TEXTURE2D_DESC1 msaaTexDesc(
  7592. DXGI_FORMAT_B8G8R8A8_UNORM,
  7593. lround(m_d3dRenderTargetSize.Width),
  7594. lround(m_d3dRenderTargetSize.Height),
  7595. 1, // arraySize
  7596. 1, // mipLevels
  7597. D3D11_BIND_RENDER_TARGET,
  7598. D3D11_USAGE_DEFAULT,
  7599. 0, // cpuAccessFlags
  7600. _sapp.sample_count,
  7601. _sapp.sample_count > 1 ? D3D11_STANDARD_MULTISAMPLE_PATTERN : 0
  7602. );
  7603. winrt::check_hresult(m_d3dDevice->CreateTexture2D1(&msaaTexDesc, nullptr, m_d3dMSAARenderTarget.put()));
  7604. winrt::check_hresult(m_d3dDevice->CreateRenderTargetView1(m_d3dMSAARenderTarget.get(), nullptr, m_d3dMSAARenderTargetView.put()));
  7605. }
  7606. // Create a depth stencil view for use with 3D rendering if needed.
  7607. CD3D11_TEXTURE2D_DESC1 depthStencilDesc(
  7608. DXGI_FORMAT_D24_UNORM_S8_UINT,
  7609. lround(m_d3dRenderTargetSize.Width),
  7610. lround(m_d3dRenderTargetSize.Height),
  7611. 1, // This depth stencil view has only one texture.
  7612. 1, // Use a single mipmap level.
  7613. D3D11_BIND_DEPTH_STENCIL,
  7614. D3D11_USAGE_DEFAULT,
  7615. 0, // cpuAccessFlag
  7616. _sapp.sample_count,
  7617. _sapp.sample_count > 1 ? D3D11_STANDARD_MULTISAMPLE_PATTERN : 0
  7618. );
  7619. winrt::check_hresult(m_d3dDevice->CreateTexture2D1(&depthStencilDesc, nullptr, m_d3dDepthStencil.put()));
  7620. CD3D11_DEPTH_STENCIL_VIEW_DESC depthStencilViewDesc(D3D11_DSV_DIMENSION_TEXTURE2D);
  7621. winrt::check_hresult(m_d3dDevice->CreateDepthStencilView(m_d3dDepthStencil.get(), nullptr, m_d3dDepthStencilView.put()));
  7622. // Set sokol window and framebuffer sizes
  7623. _sapp.window_width = (int) m_logicalSize.Width;
  7624. _sapp.window_height = (int) m_logicalSize.Height;
  7625. _sapp.framebuffer_width = lround(m_d3dRenderTargetSize.Width);
  7626. _sapp.framebuffer_height = lround(m_d3dRenderTargetSize.Height);
  7627. // Setup Sokol Context
  7628. _sapp.d3d11.rt = m_d3dRenderTarget.as<ID3D11Texture2D>().get();
  7629. _sapp.d3d11.rtv = m_d3dRenderTargetView.as<ID3D11RenderTargetView>().get();
  7630. _sapp.d3d11.ds = m_d3dDepthStencil.as<ID3D11Texture2D>().get();
  7631. _sapp.d3d11.dsv = m_d3dDepthStencilView.get();
  7632. if (_sapp.sample_count > 1) {
  7633. _sapp.d3d11.msaa_rt = m_d3dMSAARenderTarget.as<ID3D11Texture2D>().get();
  7634. _sapp.d3d11.msaa_rtv = m_d3dMSAARenderTargetView.as<ID3D11RenderTargetView>().get();
  7635. }
  7636. // Sokol app is now valid
  7637. _sapp.valid = true;
  7638. }
  7639. // Determine the dimensions of the render target and whether it will be scaled down.
  7640. void DeviceResources::UpdateRenderTargetSize() {
  7641. // Calculate the necessary render target size in pixels.
  7642. m_outputSize.Width = m_logicalSize.Width * _sapp.uwp.dpi.content_scale;
  7643. m_outputSize.Height = m_logicalSize.Height * _sapp.uwp.dpi.content_scale;
  7644. // Prevent zero size DirectX content from being created.
  7645. m_outputSize.Width = std::max(m_outputSize.Width, 1.0f);
  7646. m_outputSize.Height = std::max(m_outputSize.Height, 1.0f);
  7647. }
  7648. // This method is called when the CoreWindow is created (or re-created).
  7649. void DeviceResources::SetWindow(winrt::Windows::UI::Core::CoreWindow const& window) {
  7650. auto currentDisplayInformation = winrt::Windows::Graphics::Display::DisplayInformation::GetForCurrentView();
  7651. m_window = window;
  7652. m_logicalSize = winrt::Windows::Foundation::Size(window.Bounds().Width, window.Bounds().Height);
  7653. m_nativeOrientation = currentDisplayInformation.NativeOrientation();
  7654. m_currentOrientation = currentDisplayInformation.CurrentOrientation();
  7655. m_dpi = currentDisplayInformation.LogicalDpi();
  7656. _sapp_uwp_configure_dpi(m_dpi);
  7657. CreateWindowSizeDependentResources();
  7658. }
  7659. // This method is called in the event handler for the SizeChanged event.
  7660. void DeviceResources::SetLogicalSize(winrt::Windows::Foundation::Size logicalSize) {
  7661. if (m_logicalSize != logicalSize) {
  7662. m_logicalSize = logicalSize;
  7663. CreateWindowSizeDependentResources();
  7664. }
  7665. }
  7666. // This method is called in the event handler for the DpiChanged event.
  7667. void DeviceResources::SetDpi(float dpi) {
  7668. if (dpi != m_dpi) {
  7669. m_dpi = dpi;
  7670. _sapp_uwp_configure_dpi(m_dpi);
  7671. // When the display DPI changes, the logical size of the window (measured in Dips) also changes and needs to be updated.
  7672. auto window = m_window.get();
  7673. m_logicalSize = winrt::Windows::Foundation::Size(window.Bounds().Width, window.Bounds().Height);
  7674. CreateWindowSizeDependentResources();
  7675. }
  7676. }
  7677. // This method is called in the event handler for the OrientationChanged event.
  7678. void DeviceResources::SetCurrentOrientation(winrt::Windows::Graphics::Display::DisplayOrientations currentOrientation) {
  7679. if (m_currentOrientation != currentOrientation) {
  7680. m_currentOrientation = currentOrientation;
  7681. CreateWindowSizeDependentResources();
  7682. }
  7683. }
  7684. // This method is called in the event handler for the DisplayContentsInvalidated event.
  7685. void DeviceResources::ValidateDevice() {
  7686. // The D3D Device is no longer valid if the default adapter changed since the device
  7687. // was created or if the device has been removed.
  7688. // First, get the information for the default adapter from when the device was created.
  7689. winrt::com_ptr<IDXGIDevice3> dxgiDevice = m_d3dDevice.as< IDXGIDevice3>();
  7690. winrt::com_ptr<IDXGIAdapter> deviceAdapter;
  7691. winrt::check_hresult(dxgiDevice->GetAdapter(deviceAdapter.put()));
  7692. winrt::com_ptr<IDXGIFactory4> deviceFactory;
  7693. winrt::check_hresult(deviceAdapter->GetParent(IID_PPV_ARGS(&deviceFactory)));
  7694. winrt::com_ptr<IDXGIAdapter1> previousDefaultAdapter;
  7695. winrt::check_hresult(deviceFactory->EnumAdapters1(0, previousDefaultAdapter.put()));
  7696. DXGI_ADAPTER_DESC1 previousDesc;
  7697. winrt::check_hresult(previousDefaultAdapter->GetDesc1(&previousDesc));
  7698. // Next, get the information for the current default adapter.
  7699. winrt::com_ptr<IDXGIFactory4> currentFactory;
  7700. winrt::check_hresult(CreateDXGIFactory1(IID_PPV_ARGS(&currentFactory)));
  7701. winrt::com_ptr<IDXGIAdapter1> currentDefaultAdapter;
  7702. winrt::check_hresult(currentFactory->EnumAdapters1(0, currentDefaultAdapter.put()));
  7703. DXGI_ADAPTER_DESC1 currentDesc;
  7704. winrt::check_hresult(currentDefaultAdapter->GetDesc1(&currentDesc));
  7705. // If the adapter LUIDs don't match, or if the device reports that it has been removed,
  7706. // a new D3D device must be created.
  7707. if (previousDesc.AdapterLuid.LowPart != currentDesc.AdapterLuid.LowPart ||
  7708. previousDesc.AdapterLuid.HighPart != currentDesc.AdapterLuid.HighPart ||
  7709. FAILED(m_d3dDevice->GetDeviceRemovedReason()))
  7710. {
  7711. // Release references to resources related to the old device.
  7712. dxgiDevice = nullptr;
  7713. deviceAdapter = nullptr;
  7714. deviceFactory = nullptr;
  7715. previousDefaultAdapter = nullptr;
  7716. // Create a new device and swap chain.
  7717. HandleDeviceLost();
  7718. }
  7719. }
  7720. // Recreate all device resources and set them back to the current state.
  7721. void DeviceResources::HandleDeviceLost() {
  7722. m_swapChain = nullptr;
  7723. if (m_deviceNotify != nullptr) {
  7724. m_deviceNotify->OnDeviceLost();
  7725. }
  7726. CreateDeviceResources();
  7727. CreateWindowSizeDependentResources();
  7728. if (m_deviceNotify != nullptr) {
  7729. m_deviceNotify->OnDeviceRestored();
  7730. }
  7731. }
  7732. // Register our DeviceNotify to be informed on device lost and creation.
  7733. void DeviceResources::RegisterDeviceNotify(IDeviceNotify* deviceNotify) {
  7734. m_deviceNotify = deviceNotify;
  7735. }
  7736. // Call this method when the app suspends. It provides a hint to the driver that the app
  7737. // is entering an idle state and that temporary buffers can be reclaimed for use by other apps.
  7738. void DeviceResources::Trim() {
  7739. m_d3dDevice.as<IDXGIDevice3>()->Trim();
  7740. }
  7741. // Present the contents of the swap chain to the screen.
  7742. void DeviceResources::Present() {
  7743. // MSAA resolve if needed
  7744. if (_sapp.sample_count > 1) {
  7745. m_d3dContext->ResolveSubresource(m_d3dRenderTarget.get(), 0, m_d3dMSAARenderTarget.get(), 0, DXGI_FORMAT_B8G8R8A8_UNORM);
  7746. m_d3dContext->DiscardView1(m_d3dMSAARenderTargetView.get(), nullptr, 0);
  7747. }
  7748. // The first argument instructs DXGI to block until VSync, putting the application
  7749. // to sleep until the next VSync. This ensures we don't waste any cycles rendering
  7750. // frames that will never be displayed to the screen.
  7751. DXGI_PRESENT_PARAMETERS parameters = { 0 };
  7752. HRESULT hr = m_swapChain->Present1(1, 0, &parameters);
  7753. // Discard the contents of the render target.
  7754. // This is a valid operation only when the existing contents will be entirely
  7755. // overwritten. If dirty or scroll rects are used, this call should be removed.
  7756. m_d3dContext->DiscardView1(m_d3dRenderTargetView.get(), nullptr, 0);
  7757. // Discard the contents of the depth stencil.
  7758. m_d3dContext->DiscardView1(m_d3dDepthStencilView.get(), nullptr, 0);
  7759. // If the device was removed either by a disconnection or a driver upgrade, we
  7760. // must recreate all device resources.
  7761. if (hr == DXGI_ERROR_DEVICE_REMOVED || hr == DXGI_ERROR_DEVICE_RESET) {
  7762. HandleDeviceLost();
  7763. }
  7764. else {
  7765. winrt::check_hresult(hr);
  7766. }
  7767. }
  7768. // This method determines the rotation between the display device's native orientation and the
  7769. // current display orientation.
  7770. DXGI_MODE_ROTATION DeviceResources::ComputeDisplayRotation() {
  7771. DXGI_MODE_ROTATION rotation = DXGI_MODE_ROTATION_UNSPECIFIED;
  7772. // Note: NativeOrientation can only be Landscape or Portrait even though
  7773. // the DisplayOrientations enum has other values.
  7774. switch (m_nativeOrientation) {
  7775. case winrt::Windows::Graphics::Display::DisplayOrientations::Landscape:
  7776. switch (m_currentOrientation) {
  7777. case winrt::Windows::Graphics::Display::DisplayOrientations::Landscape:
  7778. rotation = DXGI_MODE_ROTATION_IDENTITY;
  7779. break;
  7780. case winrt::Windows::Graphics::Display::DisplayOrientations::Portrait:
  7781. rotation = DXGI_MODE_ROTATION_ROTATE270;
  7782. break;
  7783. case winrt::Windows::Graphics::Display::DisplayOrientations::LandscapeFlipped:
  7784. rotation = DXGI_MODE_ROTATION_ROTATE180;
  7785. break;
  7786. case winrt::Windows::Graphics::Display::DisplayOrientations::PortraitFlipped:
  7787. rotation = DXGI_MODE_ROTATION_ROTATE90;
  7788. break;
  7789. }
  7790. break;
  7791. case winrt::Windows::Graphics::Display::DisplayOrientations::Portrait:
  7792. switch (m_currentOrientation) {
  7793. case winrt::Windows::Graphics::Display::DisplayOrientations::Landscape:
  7794. rotation = DXGI_MODE_ROTATION_ROTATE90;
  7795. break;
  7796. case winrt::Windows::Graphics::Display::DisplayOrientations::Portrait:
  7797. rotation = DXGI_MODE_ROTATION_IDENTITY;
  7798. break;
  7799. case winrt::Windows::Graphics::Display::DisplayOrientations::LandscapeFlipped:
  7800. rotation = DXGI_MODE_ROTATION_ROTATE270;
  7801. break;
  7802. case winrt::Windows::Graphics::Display::DisplayOrientations::PortraitFlipped:
  7803. rotation = DXGI_MODE_ROTATION_ROTATE180;
  7804. break;
  7805. }
  7806. break;
  7807. }
  7808. return rotation;
  7809. }
  7810. // Check for SDK Layer support.
  7811. bool DeviceResources::SdkLayersAvailable() {
  7812. #if defined(_DEBUG)
  7813. HRESULT hr = D3D11CreateDevice(
  7814. nullptr,
  7815. D3D_DRIVER_TYPE_NULL, // There is no need to create a real hardware device.
  7816. 0,
  7817. D3D11_CREATE_DEVICE_DEBUG, // Check for the SDK layers.
  7818. nullptr, // Any feature level will do.
  7819. 0,
  7820. D3D11_SDK_VERSION, // Always set this to D3D11_SDK_VERSION for Microsoft Store apps.
  7821. nullptr, // No need to keep the D3D device reference.
  7822. nullptr, // No need to know the feature level.
  7823. nullptr // No need to keep the D3D device context reference.
  7824. );
  7825. return SUCCEEDED(hr);
  7826. #else
  7827. return false;
  7828. #endif
  7829. }
  7830. // The first method called when the IFrameworkView is being created.
  7831. void App::Initialize(winrt::Windows::ApplicationModel::Core::CoreApplicationView const& applicationView) {
  7832. // Register event handlers for app lifecycle. This example includes Activated, so that we
  7833. // can make the CoreWindow active and start rendering on the window.
  7834. applicationView.Activated({ this, &App::OnActivated });
  7835. winrt::Windows::ApplicationModel::Core::CoreApplication::Suspending({ this, &App::OnSuspending });
  7836. winrt::Windows::ApplicationModel::Core::CoreApplication::Resuming({ this, &App::OnResuming });
  7837. // At this point we have access to the device.
  7838. // We can create the device-dependent resources.
  7839. m_deviceResources = std::make_unique<DeviceResources>();
  7840. }
  7841. // Called when the CoreWindow object is created (or re-created).
  7842. void App::SetWindow(winrt::Windows::UI::Core::CoreWindow const& window) {
  7843. window.SizeChanged({ this, &App::OnWindowSizeChanged });
  7844. window.VisibilityChanged({ this, &App::OnVisibilityChanged });
  7845. window.KeyDown({ this, &App::OnKeyDown });
  7846. window.KeyUp({ this, &App::OnKeyUp });
  7847. window.CharacterReceived({ this, &App::OnCharacterReceived });
  7848. window.PointerEntered({ this, &App::OnPointerEntered });
  7849. window.PointerExited({ this, &App::OnPointerExited });
  7850. window.PointerPressed({ this, &App::OnPointerPressed });
  7851. window.PointerReleased({ this, &App::OnPointerReleased });
  7852. window.PointerMoved({ this, &App::OnPointerMoved });
  7853. window.PointerWheelChanged({ this, &App::OnPointerWheelChanged });
  7854. auto currentDisplayInformation = winrt::Windows::Graphics::Display::DisplayInformation::GetForCurrentView();
  7855. currentDisplayInformation.DpiChanged({ this, &App::OnDpiChanged });
  7856. currentDisplayInformation.OrientationChanged({ this, &App::OnOrientationChanged });
  7857. winrt::Windows::Graphics::Display::DisplayInformation::DisplayContentsInvalidated({ this, &App::OnDisplayContentsInvalidated });
  7858. winrt::Windows::UI::Core::SystemNavigationManager::GetForCurrentView().BackRequested({ this, &App::OnBackRequested });
  7859. m_deviceResources->SetWindow(window);
  7860. }
  7861. // Initializes scene resources, or loads a previously saved app state.
  7862. void App::Load(winrt::hstring const& entryPoint) {
  7863. _SOKOL_UNUSED(entryPoint);
  7864. }
  7865. // This method is called after the window becomes active.
  7866. void App::Run() {
  7867. // NOTE: UWP will simply terminate an application, it's not possible to detect when an application is being closed
  7868. while (true) {
  7869. if (m_windowVisible) {
  7870. winrt::Windows::UI::Core::CoreWindow::GetForCurrentThread().Dispatcher().ProcessEvents(winrt::Windows::UI::Core::CoreProcessEventsOption::ProcessAllIfPresent);
  7871. _sapp_frame();
  7872. m_deviceResources->Present();
  7873. }
  7874. else {
  7875. winrt::Windows::UI::Core::CoreWindow::GetForCurrentThread().Dispatcher().ProcessEvents(winrt::Windows::UI::Core::CoreProcessEventsOption::ProcessOneAndAllPending);
  7876. }
  7877. }
  7878. }
  7879. // Required for IFrameworkView.
  7880. // Terminate events do not cause Uninitialize to be called. It will be called if your IFrameworkView
  7881. // class is torn down while the app is in the foreground.
  7882. void App::Uninitialize() {
  7883. // empty
  7884. }
  7885. // Application lifecycle event handlers.
  7886. void App::OnActivated(winrt::Windows::ApplicationModel::Core::CoreApplicationView const& applicationView, winrt::Windows::ApplicationModel::Activation::IActivatedEventArgs const& args) {
  7887. _SOKOL_UNUSED(args);
  7888. _SOKOL_UNUSED(applicationView);
  7889. auto appView = winrt::Windows::UI::ViewManagement::ApplicationView::GetForCurrentView();
  7890. auto targetSize = winrt::Windows::Foundation::Size((float)_sapp.desc.width, (float)_sapp.desc.height);
  7891. appView.SetPreferredMinSize(targetSize);
  7892. appView.TryResizeView(targetSize);
  7893. // Disabling this since it can only append the title to the app name (Title - Appname).
  7894. // There's no way of just setting a string to be the window title.
  7895. //appView.Title(_sapp.window_title_wide);
  7896. // Run() won't start until the CoreWindow is activated.
  7897. winrt::Windows::UI::Core::CoreWindow::GetForCurrentThread().Activate();
  7898. if (_sapp.desc.fullscreen) {
  7899. appView.TryEnterFullScreenMode();
  7900. }
  7901. _sapp.fullscreen = appView.IsFullScreenMode();
  7902. }
  7903. void App::OnSuspending(winrt::Windows::Foundation::IInspectable const& sender, winrt::Windows::ApplicationModel::SuspendingEventArgs const& args) {
  7904. _SOKOL_UNUSED(sender);
  7905. _SOKOL_UNUSED(args);
  7906. _sapp_win32_uwp_app_event(SAPP_EVENTTYPE_SUSPENDED);
  7907. }
  7908. void App::OnResuming(winrt::Windows::Foundation::IInspectable const& sender, winrt::Windows::Foundation::IInspectable const& args) {
  7909. _SOKOL_UNUSED(args);
  7910. _SOKOL_UNUSED(sender);
  7911. _sapp_win32_uwp_app_event(SAPP_EVENTTYPE_RESUMED);
  7912. }
  7913. void App::OnWindowSizeChanged(winrt::Windows::UI::Core::CoreWindow const& sender, winrt::Windows::UI::Core::WindowSizeChangedEventArgs const& args) {
  7914. _SOKOL_UNUSED(args);
  7915. m_deviceResources->SetLogicalSize(winrt::Windows::Foundation::Size(sender.Bounds().Width, sender.Bounds().Height));
  7916. _sapp_win32_uwp_app_event(SAPP_EVENTTYPE_RESIZED);
  7917. }
  7918. void App::OnVisibilityChanged(winrt::Windows::UI::Core::CoreWindow const& sender, winrt::Windows::UI::Core::VisibilityChangedEventArgs const& args) {
  7919. _SOKOL_UNUSED(sender);
  7920. m_windowVisible = args.Visible();
  7921. _sapp_win32_uwp_app_event(m_windowVisible ? SAPP_EVENTTYPE_RESTORED : SAPP_EVENTTYPE_ICONIFIED);
  7922. }
  7923. void App::OnBackRequested(winrt::Windows::Foundation::IInspectable const& sender, winrt::Windows::UI::Core::BackRequestedEventArgs const& args) {
  7924. _SOKOL_UNUSED(sender);
  7925. args.Handled(true);
  7926. }
  7927. void App::OnKeyDown(winrt::Windows::UI::Core::CoreWindow const& sender, winrt::Windows::UI::Core::KeyEventArgs const& args) {
  7928. auto status = args.KeyStatus();
  7929. _sapp_uwp_key_event(SAPP_EVENTTYPE_KEY_DOWN, sender, args);
  7930. }
  7931. void App::OnKeyUp(winrt::Windows::UI::Core::CoreWindow const& sender, winrt::Windows::UI::Core::KeyEventArgs const& args) {
  7932. auto status = args.KeyStatus();
  7933. _sapp_uwp_key_event(SAPP_EVENTTYPE_KEY_UP, sender, args);
  7934. }
  7935. void App::OnCharacterReceived(winrt::Windows::UI::Core::CoreWindow const& sender, winrt::Windows::UI::Core::CharacterReceivedEventArgs const& args) {
  7936. _sapp_uwp_char_event(args.KeyCode(), args.KeyStatus().WasKeyDown, sender);
  7937. }
  7938. void App::OnPointerEntered(winrt::Windows::UI::Core::CoreWindow const& sender, winrt::Windows::UI::Core::PointerEventArgs const& args) {
  7939. _SOKOL_UNUSED(args);
  7940. _sapp.uwp.mouse_tracked = true;
  7941. _sapp_uwp_mouse_event(SAPP_EVENTTYPE_MOUSE_ENTER, SAPP_MOUSEBUTTON_INVALID, sender);
  7942. }
  7943. void App::OnPointerExited(winrt::Windows::UI::Core::CoreWindow const& sender, winrt::Windows::UI::Core::PointerEventArgs const& args) {
  7944. _SOKOL_UNUSED(args);
  7945. _sapp.uwp.mouse_tracked = false;
  7946. _sapp_uwp_mouse_event(SAPP_EVENTTYPE_MOUSE_LEAVE, SAPP_MOUSEBUTTON_INVALID, sender);
  7947. }
  7948. void App::OnPointerPressed(winrt::Windows::UI::Core::CoreWindow const& sender, winrt::Windows::UI::Core::PointerEventArgs const& args) {
  7949. _sapp_uwp_extract_mouse_button_events(sender, args);
  7950. }
  7951. // NOTE: for some reason this event handler is never called??
  7952. void App::OnPointerReleased(winrt::Windows::UI::Core::CoreWindow const& sender, winrt::Windows::UI::Core::PointerEventArgs const& args) {
  7953. _sapp_uwp_extract_mouse_button_events(sender, args);
  7954. }
  7955. void App::OnPointerMoved(winrt::Windows::UI::Core::CoreWindow const& sender, winrt::Windows::UI::Core::PointerEventArgs const& args) {
  7956. auto position = args.CurrentPoint().Position();
  7957. const float new_x = (float)(int)(position.X * _sapp.uwp.dpi.mouse_scale + 0.5f);
  7958. const float new_y = (float)(int)(position.Y * _sapp.uwp.dpi.mouse_scale + 0.5f);
  7959. // don't update dx/dy in the very first event
  7960. if (_sapp.mouse.pos_valid) {
  7961. _sapp.mouse.dx = new_x - _sapp.mouse.x;
  7962. _sapp.mouse.dy = new_y - _sapp.mouse.y;
  7963. }
  7964. _sapp.mouse.x = new_x;
  7965. _sapp.mouse.y = new_y;
  7966. _sapp.mouse.pos_valid = true;
  7967. if (!_sapp.uwp.mouse_tracked) {
  7968. _sapp.uwp.mouse_tracked = true;
  7969. _sapp_uwp_mouse_event(SAPP_EVENTTYPE_MOUSE_ENTER, SAPP_MOUSEBUTTON_INVALID, sender);
  7970. }
  7971. _sapp_uwp_mouse_event(SAPP_EVENTTYPE_MOUSE_MOVE, SAPP_MOUSEBUTTON_INVALID, sender);
  7972. // HACK for detecting multiple mouse button presses
  7973. _sapp_uwp_extract_mouse_button_events(sender, args);
  7974. }
  7975. void App::OnPointerWheelChanged(winrt::Windows::UI::Core::CoreWindow const& sender, winrt::Windows::UI::Core::PointerEventArgs const& args) {
  7976. auto properties = args.CurrentPoint().Properties();
  7977. _sapp_uwp_scroll_event((float)properties.MouseWheelDelta(), properties.IsHorizontalMouseWheel(), sender);
  7978. }
  7979. void App::OnDpiChanged(winrt::Windows::Graphics::Display::DisplayInformation const& sender, winrt::Windows::Foundation::IInspectable const& args) {
  7980. // NOTE: UNTESTED
  7981. _SOKOL_UNUSED(args);
  7982. m_deviceResources->SetDpi(sender.LogicalDpi());
  7983. _sapp_win32_uwp_app_event(SAPP_EVENTTYPE_RESIZED);
  7984. }
  7985. void App::OnOrientationChanged(winrt::Windows::Graphics::Display::DisplayInformation const& sender, winrt::Windows::Foundation::IInspectable const& args) {
  7986. // NOTE: UNTESTED
  7987. _SOKOL_UNUSED(args);
  7988. m_deviceResources->SetCurrentOrientation(sender.CurrentOrientation());
  7989. _sapp_win32_uwp_app_event(SAPP_EVENTTYPE_RESIZED);
  7990. }
  7991. void App::OnDisplayContentsInvalidated(winrt::Windows::Graphics::Display::DisplayInformation const& sender, winrt::Windows::Foundation::IInspectable const& args) {
  7992. // NOTE: UNTESTED
  7993. _SOKOL_UNUSED(args);
  7994. _SOKOL_UNUSED(sender);
  7995. m_deviceResources->ValidateDevice();
  7996. }
  7997. } /* End empty namespace */
  7998. _SOKOL_PRIVATE void _sapp_uwp_run(const sapp_desc* desc) {
  7999. _sapp_init_state(desc);
  8000. _sapp_win32_uwp_init_keytable();
  8001. _sapp_win32_uwp_utf8_to_wide(_sapp.window_title, _sapp.window_title_wide, sizeof(_sapp.window_title_wide));
  8002. winrt::Windows::ApplicationModel::Core::CoreApplication::Run(winrt::make<App>());
  8003. }
  8004. #if !defined(SOKOL_NO_ENTRY)
  8005. #if defined(UNICODE)
  8006. int WINAPI wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPWSTR lpCmdLine, _In_ int nCmdShow) {
  8007. #else
  8008. int WINAPI WinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPSTR lpCmdLine, _In_ int nCmdShow) {
  8009. #endif
  8010. _SOKOL_UNUSED(hInstance);
  8011. _SOKOL_UNUSED(hPrevInstance);
  8012. _SOKOL_UNUSED(lpCmdLine);
  8013. _SOKOL_UNUSED(nCmdShow);
  8014. sapp_desc desc = sokol_main(0, nullptr);
  8015. _sapp_uwp_run(&desc);
  8016. return 0;
  8017. }
  8018. #endif /* SOKOL_NO_ENTRY */
  8019. #endif /* _SAPP_UWP */
  8020. /*== Android ================================================================*/
  8021. #if defined(_SAPP_ANDROID)
  8022. /* android loop thread */
  8023. _SOKOL_PRIVATE bool _sapp_android_init_egl(void) {
  8024. SOKOL_ASSERT(_sapp.android.display == EGL_NO_DISPLAY);
  8025. SOKOL_ASSERT(_sapp.android.context == EGL_NO_CONTEXT);
  8026. EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
  8027. if (display == EGL_NO_DISPLAY) {
  8028. return false;
  8029. }
  8030. if (eglInitialize(display, NULL, NULL) == EGL_FALSE) {
  8031. return false;
  8032. }
  8033. EGLint alpha_size = _sapp.desc.alpha ? 8 : 0;
  8034. const EGLint cfg_attributes[] = {
  8035. EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
  8036. EGL_RED_SIZE, 8,
  8037. EGL_GREEN_SIZE, 8,
  8038. EGL_BLUE_SIZE, 8,
  8039. EGL_ALPHA_SIZE, alpha_size,
  8040. EGL_DEPTH_SIZE, 16,
  8041. EGL_STENCIL_SIZE, 0,
  8042. EGL_NONE,
  8043. };
  8044. EGLConfig available_cfgs[32];
  8045. EGLint cfg_count;
  8046. eglChooseConfig(display, cfg_attributes, available_cfgs, 32, &cfg_count);
  8047. SOKOL_ASSERT(cfg_count > 0);
  8048. SOKOL_ASSERT(cfg_count <= 32);
  8049. /* find config with 8-bit rgb buffer if available, ndk sample does not trust egl spec */
  8050. EGLConfig config;
  8051. bool exact_cfg_found = false;
  8052. for (int i = 0; i < cfg_count; ++i) {
  8053. EGLConfig c = available_cfgs[i];
  8054. EGLint r, g, b, a, d;
  8055. if (eglGetConfigAttrib(display, c, EGL_RED_SIZE, &r) == EGL_TRUE &&
  8056. eglGetConfigAttrib(display, c, EGL_GREEN_SIZE, &g) == EGL_TRUE &&
  8057. eglGetConfigAttrib(display, c, EGL_BLUE_SIZE, &b) == EGL_TRUE &&
  8058. eglGetConfigAttrib(display, c, EGL_ALPHA_SIZE, &a) == EGL_TRUE &&
  8059. eglGetConfigAttrib(display, c, EGL_DEPTH_SIZE, &d) == EGL_TRUE &&
  8060. r == 8 && g == 8 && b == 8 && (alpha_size == 0 || a == alpha_size) && d == 16) {
  8061. exact_cfg_found = true;
  8062. config = c;
  8063. break;
  8064. }
  8065. }
  8066. if (!exact_cfg_found) {
  8067. config = available_cfgs[0];
  8068. }
  8069. EGLint ctx_attributes[] = {
  8070. #if defined(SOKOL_GLES3)
  8071. EGL_CONTEXT_CLIENT_VERSION, _sapp.desc.gl_force_gles2 ? 2 : 3,
  8072. #else
  8073. EGL_CONTEXT_CLIENT_VERSION, 2,
  8074. #endif
  8075. EGL_NONE,
  8076. };
  8077. EGLContext context = eglCreateContext(display, config, EGL_NO_CONTEXT, ctx_attributes);
  8078. if (context == EGL_NO_CONTEXT) {
  8079. return false;
  8080. }
  8081. _sapp.android.config = config;
  8082. _sapp.android.display = display;
  8083. _sapp.android.context = context;
  8084. return true;
  8085. }
  8086. _SOKOL_PRIVATE void _sapp_android_cleanup_egl(void) {
  8087. if (_sapp.android.display != EGL_NO_DISPLAY) {
  8088. eglMakeCurrent(_sapp.android.display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
  8089. if (_sapp.android.surface != EGL_NO_SURFACE) {
  8090. SOKOL_LOG("Destroying egl surface");
  8091. eglDestroySurface(_sapp.android.display, _sapp.android.surface);
  8092. _sapp.android.surface = EGL_NO_SURFACE;
  8093. }
  8094. if (_sapp.android.context != EGL_NO_CONTEXT) {
  8095. SOKOL_LOG("Destroying egl context");
  8096. eglDestroyContext(_sapp.android.display, _sapp.android.context);
  8097. _sapp.android.context = EGL_NO_CONTEXT;
  8098. }
  8099. SOKOL_LOG("Terminating egl display");
  8100. eglTerminate(_sapp.android.display);
  8101. _sapp.android.display = EGL_NO_DISPLAY;
  8102. }
  8103. }
  8104. _SOKOL_PRIVATE bool _sapp_android_init_egl_surface(ANativeWindow* window) {
  8105. SOKOL_ASSERT(_sapp.android.display != EGL_NO_DISPLAY);
  8106. SOKOL_ASSERT(_sapp.android.context != EGL_NO_CONTEXT);
  8107. SOKOL_ASSERT(_sapp.android.surface == EGL_NO_SURFACE);
  8108. SOKOL_ASSERT(window);
  8109. /* TODO: set window flags */
  8110. /* ANativeActivity_setWindowFlags(activity, AWINDOW_FLAG_KEEP_SCREEN_ON, 0); */
  8111. /* create egl surface and make it current */
  8112. EGLSurface surface = eglCreateWindowSurface(_sapp.android.display, _sapp.android.config, window, NULL);
  8113. if (surface == EGL_NO_SURFACE) {
  8114. return false;
  8115. }
  8116. if (eglMakeCurrent(_sapp.android.display, surface, surface, _sapp.android.context) == EGL_FALSE) {
  8117. return false;
  8118. }
  8119. _sapp.android.surface = surface;
  8120. return true;
  8121. }
  8122. _SOKOL_PRIVATE void _sapp_android_cleanup_egl_surface(void) {
  8123. if (_sapp.android.display == EGL_NO_DISPLAY) {
  8124. return;
  8125. }
  8126. eglMakeCurrent(_sapp.android.display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
  8127. if (_sapp.android.surface != EGL_NO_SURFACE) {
  8128. eglDestroySurface(_sapp.android.display, _sapp.android.surface);
  8129. _sapp.android.surface = EGL_NO_SURFACE;
  8130. }
  8131. }
  8132. _SOKOL_PRIVATE void _sapp_android_app_event(sapp_event_type type) {
  8133. if (_sapp_events_enabled()) {
  8134. _sapp_init_event(type);
  8135. SOKOL_LOG("event_cb()");
  8136. _sapp_call_event(&_sapp.event);
  8137. }
  8138. }
  8139. _SOKOL_PRIVATE void _sapp_android_update_dimensions(ANativeWindow* window, bool force_update) {
  8140. SOKOL_ASSERT(_sapp.android.display != EGL_NO_DISPLAY);
  8141. SOKOL_ASSERT(_sapp.android.context != EGL_NO_CONTEXT);
  8142. SOKOL_ASSERT(_sapp.android.surface != EGL_NO_SURFACE);
  8143. SOKOL_ASSERT(window);
  8144. const int32_t win_w = ANativeWindow_getWidth(window);
  8145. const int32_t win_h = ANativeWindow_getHeight(window);
  8146. SOKOL_ASSERT(win_w >= 0 && win_h >= 0);
  8147. const bool win_changed = (win_w != _sapp.window_width) || (win_h != _sapp.window_height);
  8148. _sapp.window_width = win_w;
  8149. _sapp.window_height = win_h;
  8150. if (win_changed || force_update) {
  8151. if (!_sapp.desc.high_dpi) {
  8152. const int32_t buf_w = win_w / 2;
  8153. const int32_t buf_h = win_h / 2;
  8154. EGLint format;
  8155. EGLBoolean egl_result = eglGetConfigAttrib(_sapp.android.display, _sapp.android.config, EGL_NATIVE_VISUAL_ID, &format);
  8156. SOKOL_ASSERT(egl_result == EGL_TRUE);
  8157. /* NOTE: calling ANativeWindow_setBuffersGeometry() with the same dimensions
  8158. as the ANativeWindow size results in weird display artefacts, that's
  8159. why it's only called when the buffer geometry is different from
  8160. the window size
  8161. */
  8162. int32_t result = ANativeWindow_setBuffersGeometry(window, buf_w, buf_h, format);
  8163. SOKOL_ASSERT(result == 0);
  8164. }
  8165. }
  8166. /* query surface size */
  8167. EGLint fb_w, fb_h;
  8168. EGLBoolean egl_result_w = eglQuerySurface(_sapp.android.display, _sapp.android.surface, EGL_WIDTH, &fb_w);
  8169. EGLBoolean egl_result_h = eglQuerySurface(_sapp.android.display, _sapp.android.surface, EGL_HEIGHT, &fb_h);
  8170. SOKOL_ASSERT(egl_result_w == EGL_TRUE);
  8171. SOKOL_ASSERT(egl_result_h == EGL_TRUE);
  8172. const bool fb_changed = (fb_w != _sapp.framebuffer_width) || (fb_h != _sapp.framebuffer_height);
  8173. _sapp.framebuffer_width = fb_w;
  8174. _sapp.framebuffer_height = fb_h;
  8175. _sapp.dpi_scale = (float)_sapp.framebuffer_width / (float)_sapp.window_width;
  8176. if (win_changed || fb_changed || force_update) {
  8177. if (!_sapp.first_frame) {
  8178. SOKOL_LOG("SAPP_EVENTTYPE_RESIZED");
  8179. _sapp_android_app_event(SAPP_EVENTTYPE_RESIZED);
  8180. }
  8181. }
  8182. }
  8183. _SOKOL_PRIVATE void _sapp_android_cleanup(void) {
  8184. SOKOL_LOG("Cleaning up");
  8185. if (_sapp.android.surface != EGL_NO_SURFACE) {
  8186. /* egl context is bound, cleanup gracefully */
  8187. if (_sapp.init_called && !_sapp.cleanup_called) {
  8188. SOKOL_LOG("cleanup_cb()");
  8189. _sapp_call_cleanup();
  8190. }
  8191. }
  8192. /* always try to cleanup by destroying egl context */
  8193. _sapp_android_cleanup_egl();
  8194. }
  8195. _SOKOL_PRIVATE void _sapp_android_shutdown(void) {
  8196. /* try to cleanup while we still have a surface and can call cleanup_cb() */
  8197. _sapp_android_cleanup();
  8198. /* request exit */
  8199. ANativeActivity_finish(_sapp.android.activity);
  8200. }
  8201. _SOKOL_PRIVATE void _sapp_android_frame(void) {
  8202. SOKOL_ASSERT(_sapp.android.display != EGL_NO_DISPLAY);
  8203. SOKOL_ASSERT(_sapp.android.context != EGL_NO_CONTEXT);
  8204. SOKOL_ASSERT(_sapp.android.surface != EGL_NO_SURFACE);
  8205. _sapp_android_update_dimensions(_sapp.android.current.window, false);
  8206. _sapp_frame();
  8207. eglSwapBuffers(_sapp.android.display, _sapp.android.surface);
  8208. }
  8209. _SOKOL_PRIVATE bool _sapp_android_touch_event(const AInputEvent* e) {
  8210. if (AInputEvent_getType(e) != AINPUT_EVENT_TYPE_MOTION) {
  8211. return false;
  8212. }
  8213. if (!_sapp_events_enabled()) {
  8214. return false;
  8215. }
  8216. int32_t action_idx = AMotionEvent_getAction(e);
  8217. int32_t action = action_idx & AMOTION_EVENT_ACTION_MASK;
  8218. sapp_event_type type = SAPP_EVENTTYPE_INVALID;
  8219. switch (action) {
  8220. case AMOTION_EVENT_ACTION_DOWN:
  8221. SOKOL_LOG("Touch: down");
  8222. case AMOTION_EVENT_ACTION_POINTER_DOWN:
  8223. SOKOL_LOG("Touch: ptr down");
  8224. type = SAPP_EVENTTYPE_TOUCHES_BEGAN;
  8225. break;
  8226. case AMOTION_EVENT_ACTION_MOVE:
  8227. type = SAPP_EVENTTYPE_TOUCHES_MOVED;
  8228. break;
  8229. case AMOTION_EVENT_ACTION_UP:
  8230. SOKOL_LOG("Touch: up");
  8231. case AMOTION_EVENT_ACTION_POINTER_UP:
  8232. SOKOL_LOG("Touch: ptr up");
  8233. type = SAPP_EVENTTYPE_TOUCHES_ENDED;
  8234. break;
  8235. case AMOTION_EVENT_ACTION_CANCEL:
  8236. SOKOL_LOG("Touch: cancel");
  8237. type = SAPP_EVENTTYPE_TOUCHES_CANCELLED;
  8238. break;
  8239. default:
  8240. break;
  8241. }
  8242. if (type == SAPP_EVENTTYPE_INVALID) {
  8243. return false;
  8244. }
  8245. int32_t idx = action_idx >> AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT;
  8246. _sapp_init_event(type);
  8247. _sapp.event.num_touches = (int)AMotionEvent_getPointerCount(e);
  8248. if (_sapp.event.num_touches > SAPP_MAX_TOUCHPOINTS) {
  8249. _sapp.event.num_touches = SAPP_MAX_TOUCHPOINTS;
  8250. }
  8251. for (int32_t i = 0; i < _sapp.event.num_touches; i++) {
  8252. sapp_touchpoint* dst = &_sapp.event.touches[i];
  8253. dst->identifier = (uintptr_t)AMotionEvent_getPointerId(e, (size_t)i);
  8254. dst->pos_x = (AMotionEvent_getRawX(e, (size_t)i) / _sapp.window_width) * _sapp.framebuffer_width;
  8255. dst->pos_y = (AMotionEvent_getRawY(e, (size_t)i) / _sapp.window_height) * _sapp.framebuffer_height;
  8256. if (action == AMOTION_EVENT_ACTION_POINTER_DOWN ||
  8257. action == AMOTION_EVENT_ACTION_POINTER_UP) {
  8258. dst->changed = (i == idx);
  8259. } else {
  8260. dst->changed = true;
  8261. }
  8262. }
  8263. _sapp_call_event(&_sapp.event);
  8264. return true;
  8265. }
  8266. _SOKOL_PRIVATE bool _sapp_android_key_event(const AInputEvent* e) {
  8267. if (AInputEvent_getType(e) != AINPUT_EVENT_TYPE_KEY) {
  8268. return false;
  8269. }
  8270. if (AKeyEvent_getKeyCode(e) == AKEYCODE_BACK) {
  8271. /* FIXME: this should be hooked into a "really quit?" mechanism
  8272. so the app can ask the user for confirmation, this is currently
  8273. generally missing in sokol_app.h
  8274. */
  8275. _sapp_android_shutdown();
  8276. return true;
  8277. }
  8278. return false;
  8279. }
  8280. _SOKOL_PRIVATE int _sapp_android_input_cb(int fd, int events, void* data) {
  8281. if ((events & ALOOPER_EVENT_INPUT) == 0) {
  8282. SOKOL_LOG("_sapp_android_input_cb() encountered unsupported event");
  8283. return 1;
  8284. }
  8285. SOKOL_ASSERT(_sapp.android.current.input);
  8286. AInputEvent* event = NULL;
  8287. while (AInputQueue_getEvent(_sapp.android.current.input, &event) >= 0) {
  8288. if (AInputQueue_preDispatchEvent(_sapp.android.current.input, event) != 0) {
  8289. continue;
  8290. }
  8291. int32_t handled = 0;
  8292. if (_sapp_android_touch_event(event) || _sapp_android_key_event(event)) {
  8293. handled = 1;
  8294. }
  8295. AInputQueue_finishEvent(_sapp.android.current.input, event, handled);
  8296. }
  8297. return 1;
  8298. }
  8299. _SOKOL_PRIVATE int _sapp_android_main_cb(int fd, int events, void* data) {
  8300. if ((events & ALOOPER_EVENT_INPUT) == 0) {
  8301. SOKOL_LOG("_sapp_android_main_cb() encountered unsupported event");
  8302. return 1;
  8303. }
  8304. _sapp_android_msg_t msg;
  8305. if (read(fd, &msg, sizeof(msg)) != sizeof(msg)) {
  8306. SOKOL_LOG("Could not write to read_from_main_fd");
  8307. return 1;
  8308. }
  8309. pthread_mutex_lock(&_sapp.android.pt.mutex);
  8310. switch (msg) {
  8311. case _SOKOL_ANDROID_MSG_CREATE:
  8312. {
  8313. SOKOL_LOG("MSG_CREATE");
  8314. SOKOL_ASSERT(!_sapp.valid);
  8315. bool result = _sapp_android_init_egl();
  8316. SOKOL_ASSERT(result);
  8317. _sapp.valid = true;
  8318. _sapp.android.has_created = true;
  8319. }
  8320. break;
  8321. case _SOKOL_ANDROID_MSG_RESUME:
  8322. SOKOL_LOG("MSG_RESUME");
  8323. _sapp.android.has_resumed = true;
  8324. _sapp_android_app_event(SAPP_EVENTTYPE_RESUMED);
  8325. break;
  8326. case _SOKOL_ANDROID_MSG_PAUSE:
  8327. SOKOL_LOG("MSG_PAUSE");
  8328. _sapp.android.has_resumed = false;
  8329. _sapp_android_app_event(SAPP_EVENTTYPE_SUSPENDED);
  8330. break;
  8331. case _SOKOL_ANDROID_MSG_FOCUS:
  8332. SOKOL_LOG("MSG_FOCUS");
  8333. _sapp.android.has_focus = true;
  8334. break;
  8335. case _SOKOL_ANDROID_MSG_NO_FOCUS:
  8336. SOKOL_LOG("MSG_NO_FOCUS");
  8337. _sapp.android.has_focus = false;
  8338. break;
  8339. case _SOKOL_ANDROID_MSG_SET_NATIVE_WINDOW:
  8340. SOKOL_LOG("MSG_SET_NATIVE_WINDOW");
  8341. if (_sapp.android.current.window != _sapp.android.pending.window) {
  8342. if (_sapp.android.current.window != NULL) {
  8343. _sapp_android_cleanup_egl_surface();
  8344. }
  8345. if (_sapp.android.pending.window != NULL) {
  8346. SOKOL_LOG("Creating egl surface ...");
  8347. if (_sapp_android_init_egl_surface(_sapp.android.pending.window)) {
  8348. SOKOL_LOG("... ok!");
  8349. _sapp_android_update_dimensions(_sapp.android.pending.window, true);
  8350. } else {
  8351. SOKOL_LOG("... failed!");
  8352. _sapp_android_shutdown();
  8353. }
  8354. }
  8355. }
  8356. _sapp.android.current.window = _sapp.android.pending.window;
  8357. break;
  8358. case _SOKOL_ANDROID_MSG_SET_INPUT_QUEUE:
  8359. SOKOL_LOG("MSG_SET_INPUT_QUEUE");
  8360. if (_sapp.android.current.input != _sapp.android.pending.input) {
  8361. if (_sapp.android.current.input != NULL) {
  8362. AInputQueue_detachLooper(_sapp.android.current.input);
  8363. }
  8364. if (_sapp.android.pending.input != NULL) {
  8365. AInputQueue_attachLooper(
  8366. _sapp.android.pending.input,
  8367. _sapp.android.looper,
  8368. ALOOPER_POLL_CALLBACK,
  8369. _sapp_android_input_cb,
  8370. NULL); /* data */
  8371. }
  8372. }
  8373. _sapp.android.current.input = _sapp.android.pending.input;
  8374. break;
  8375. case _SOKOL_ANDROID_MSG_DESTROY:
  8376. SOKOL_LOG("MSG_DESTROY");
  8377. _sapp_android_cleanup();
  8378. _sapp.valid = false;
  8379. _sapp.android.is_thread_stopping = true;
  8380. break;
  8381. default:
  8382. SOKOL_LOG("Unknown msg type received");
  8383. break;
  8384. }
  8385. pthread_cond_broadcast(&_sapp.android.pt.cond); /* signal "received" */
  8386. pthread_mutex_unlock(&_sapp.android.pt.mutex);
  8387. return 1;
  8388. }
  8389. _SOKOL_PRIVATE bool _sapp_android_should_update(void) {
  8390. bool is_in_front = _sapp.android.has_resumed && _sapp.android.has_focus;
  8391. bool has_surface = _sapp.android.surface != EGL_NO_SURFACE;
  8392. return is_in_front && has_surface;
  8393. }
  8394. _SOKOL_PRIVATE void _sapp_android_show_keyboard(bool shown) {
  8395. SOKOL_ASSERT(_sapp.valid);
  8396. /* This seems to be broken in the NDK, but there is (a very cumbersome) workaround... */
  8397. if (shown) {
  8398. SOKOL_LOG("Showing keyboard");
  8399. ANativeActivity_showSoftInput(_sapp.android.activity, ANATIVEACTIVITY_SHOW_SOFT_INPUT_FORCED);
  8400. } else {
  8401. SOKOL_LOG("Hiding keyboard");
  8402. ANativeActivity_hideSoftInput(_sapp.android.activity, ANATIVEACTIVITY_HIDE_SOFT_INPUT_NOT_ALWAYS);
  8403. }
  8404. }
  8405. _SOKOL_PRIVATE void* _sapp_android_loop(void* arg) {
  8406. _SOKOL_UNUSED(arg);
  8407. SOKOL_LOG("Loop thread started");
  8408. _sapp.android.looper = ALooper_prepare(0 /* or ALOOPER_PREPARE_ALLOW_NON_CALLBACKS*/);
  8409. ALooper_addFd(_sapp.android.looper,
  8410. _sapp.android.pt.read_from_main_fd,
  8411. ALOOPER_POLL_CALLBACK,
  8412. ALOOPER_EVENT_INPUT,
  8413. _sapp_android_main_cb,
  8414. NULL); /* data */
  8415. /* signal start to main thread */
  8416. pthread_mutex_lock(&_sapp.android.pt.mutex);
  8417. _sapp.android.is_thread_started = true;
  8418. pthread_cond_broadcast(&_sapp.android.pt.cond);
  8419. pthread_mutex_unlock(&_sapp.android.pt.mutex);
  8420. /* main loop */
  8421. while (!_sapp.android.is_thread_stopping) {
  8422. /* sokol frame */
  8423. if (_sapp_android_should_update()) {
  8424. _sapp_android_frame();
  8425. }
  8426. /* process all events (or stop early if app is requested to quit) */
  8427. bool process_events = true;
  8428. while (process_events && !_sapp.android.is_thread_stopping) {
  8429. bool block_until_event = !_sapp.android.is_thread_stopping && !_sapp_android_should_update();
  8430. process_events = ALooper_pollOnce(block_until_event ? -1 : 0, NULL, NULL, NULL) == ALOOPER_POLL_CALLBACK;
  8431. }
  8432. }
  8433. /* cleanup thread */
  8434. if (_sapp.android.current.input != NULL) {
  8435. AInputQueue_detachLooper(_sapp.android.current.input);
  8436. }
  8437. /* the following causes heap corruption on exit, why??
  8438. ALooper_removeFd(_sapp.android.looper, _sapp.android.pt.read_from_main_fd);
  8439. ALooper_release(_sapp.android.looper);*/
  8440. /* signal "destroyed" */
  8441. pthread_mutex_lock(&_sapp.android.pt.mutex);
  8442. _sapp.android.is_thread_stopped = true;
  8443. pthread_cond_broadcast(&_sapp.android.pt.cond);
  8444. pthread_mutex_unlock(&_sapp.android.pt.mutex);
  8445. SOKOL_LOG("Loop thread done");
  8446. return NULL;
  8447. }
  8448. /* android main/ui thread */
  8449. _SOKOL_PRIVATE void _sapp_android_msg(_sapp_android_msg_t msg) {
  8450. if (write(_sapp.android.pt.write_from_main_fd, &msg, sizeof(msg)) != sizeof(msg)) {
  8451. SOKOL_LOG("Could not write to write_from_main_fd");
  8452. }
  8453. }
  8454. _SOKOL_PRIVATE void _sapp_android_on_start(ANativeActivity* activity) {
  8455. SOKOL_LOG("NativeActivity onStart()");
  8456. }
  8457. _SOKOL_PRIVATE void _sapp_android_on_resume(ANativeActivity* activity) {
  8458. SOKOL_LOG("NativeActivity onResume()");
  8459. _sapp_android_msg(_SOKOL_ANDROID_MSG_RESUME);
  8460. }
  8461. _SOKOL_PRIVATE void* _sapp_android_on_save_instance_state(ANativeActivity* activity, size_t* out_size) {
  8462. SOKOL_LOG("NativeActivity onSaveInstanceState()");
  8463. *out_size = 0;
  8464. return NULL;
  8465. }
  8466. _SOKOL_PRIVATE void _sapp_android_on_window_focus_changed(ANativeActivity* activity, int has_focus) {
  8467. SOKOL_LOG("NativeActivity onWindowFocusChanged()");
  8468. if (has_focus) {
  8469. _sapp_android_msg(_SOKOL_ANDROID_MSG_FOCUS);
  8470. } else {
  8471. _sapp_android_msg(_SOKOL_ANDROID_MSG_NO_FOCUS);
  8472. }
  8473. }
  8474. _SOKOL_PRIVATE void _sapp_android_on_pause(ANativeActivity* activity) {
  8475. SOKOL_LOG("NativeActivity onPause()");
  8476. _sapp_android_msg(_SOKOL_ANDROID_MSG_PAUSE);
  8477. }
  8478. _SOKOL_PRIVATE void _sapp_android_on_stop(ANativeActivity* activity) {
  8479. SOKOL_LOG("NativeActivity onStop()");
  8480. }
  8481. _SOKOL_PRIVATE void _sapp_android_msg_set_native_window(ANativeWindow* window) {
  8482. pthread_mutex_lock(&_sapp.android.pt.mutex);
  8483. _sapp.android.pending.window = window;
  8484. _sapp_android_msg(_SOKOL_ANDROID_MSG_SET_NATIVE_WINDOW);
  8485. while (_sapp.android.current.window != window) {
  8486. pthread_cond_wait(&_sapp.android.pt.cond, &_sapp.android.pt.mutex);
  8487. }
  8488. pthread_mutex_unlock(&_sapp.android.pt.mutex);
  8489. }
  8490. _SOKOL_PRIVATE void _sapp_android_on_native_window_created(ANativeActivity* activity, ANativeWindow* window) {
  8491. SOKOL_LOG("NativeActivity onNativeWindowCreated()");
  8492. _sapp_android_msg_set_native_window(window);
  8493. }
  8494. _SOKOL_PRIVATE void _sapp_android_on_native_window_destroyed(ANativeActivity* activity, ANativeWindow* window) {
  8495. SOKOL_LOG("NativeActivity onNativeWindowDestroyed()");
  8496. _sapp_android_msg_set_native_window(NULL);
  8497. }
  8498. _SOKOL_PRIVATE void _sapp_android_msg_set_input_queue(AInputQueue* input) {
  8499. pthread_mutex_lock(&_sapp.android.pt.mutex);
  8500. _sapp.android.pending.input = input;
  8501. _sapp_android_msg(_SOKOL_ANDROID_MSG_SET_INPUT_QUEUE);
  8502. while (_sapp.android.current.input != input) {
  8503. pthread_cond_wait(&_sapp.android.pt.cond, &_sapp.android.pt.mutex);
  8504. }
  8505. pthread_mutex_unlock(&_sapp.android.pt.mutex);
  8506. }
  8507. _SOKOL_PRIVATE void _sapp_android_on_input_queue_created(ANativeActivity* activity, AInputQueue* queue) {
  8508. SOKOL_LOG("NativeActivity onInputQueueCreated()");
  8509. _sapp_android_msg_set_input_queue(queue);
  8510. }
  8511. _SOKOL_PRIVATE void _sapp_android_on_input_queue_destroyed(ANativeActivity* activity, AInputQueue* queue) {
  8512. SOKOL_LOG("NativeActivity onInputQueueDestroyed()");
  8513. _sapp_android_msg_set_input_queue(NULL);
  8514. }
  8515. _SOKOL_PRIVATE void _sapp_android_on_config_changed(ANativeActivity* activity) {
  8516. SOKOL_LOG("NativeActivity onConfigurationChanged()");
  8517. /* see android:configChanges in manifest */
  8518. }
  8519. _SOKOL_PRIVATE void _sapp_android_on_low_memory(ANativeActivity* activity) {
  8520. SOKOL_LOG("NativeActivity onLowMemory()");
  8521. }
  8522. _SOKOL_PRIVATE void _sapp_android_on_destroy(ANativeActivity* activity) {
  8523. /*
  8524. * For some reason even an empty app using nativeactivity.h will crash (WIN DEATH)
  8525. * on my device (Moto X 2nd gen) when the app is removed from the task view
  8526. * (TaskStackView: onTaskViewDismissed).
  8527. *
  8528. * However, if ANativeActivity_finish() is explicitly called from for example
  8529. * _sapp_android_on_stop(), the crash disappears. Is this a bug in NativeActivity?
  8530. */
  8531. SOKOL_LOG("NativeActivity onDestroy()");
  8532. /* send destroy msg */
  8533. pthread_mutex_lock(&_sapp.android.pt.mutex);
  8534. _sapp_android_msg(_SOKOL_ANDROID_MSG_DESTROY);
  8535. while (!_sapp.android.is_thread_stopped) {
  8536. pthread_cond_wait(&_sapp.android.pt.cond, &_sapp.android.pt.mutex);
  8537. }
  8538. pthread_mutex_unlock(&_sapp.android.pt.mutex);
  8539. /* clean up main thread */
  8540. pthread_cond_destroy(&_sapp.android.pt.cond);
  8541. pthread_mutex_destroy(&_sapp.android.pt.mutex);
  8542. close(_sapp.android.pt.read_from_main_fd);
  8543. close(_sapp.android.pt.write_from_main_fd);
  8544. SOKOL_LOG("NativeActivity done");
  8545. /* this is a bit naughty, but causes a clean restart of the app (static globals are reset) */
  8546. exit(0);
  8547. }
  8548. JNIEXPORT
  8549. void ANativeActivity_onCreate(ANativeActivity* activity, void* saved_state, size_t saved_state_size) {
  8550. SOKOL_LOG("NativeActivity onCreate()");
  8551. sapp_desc desc = sokol_main(0, NULL);
  8552. _sapp_init_state(&desc);
  8553. /* start loop thread */
  8554. _sapp.android.activity = activity;
  8555. int pipe_fd[2];
  8556. if (pipe(pipe_fd) != 0) {
  8557. SOKOL_LOG("Could not create thread pipe");
  8558. return;
  8559. }
  8560. _sapp.android.pt.read_from_main_fd = pipe_fd[0];
  8561. _sapp.android.pt.write_from_main_fd = pipe_fd[1];
  8562. pthread_mutex_init(&_sapp.android.pt.mutex, NULL);
  8563. pthread_cond_init(&_sapp.android.pt.cond, NULL);
  8564. pthread_attr_t attr;
  8565. pthread_attr_init(&attr);
  8566. pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
  8567. pthread_create(&_sapp.android.pt.thread, &attr, _sapp_android_loop, 0);
  8568. pthread_attr_destroy(&attr);
  8569. /* wait until main loop has started */
  8570. pthread_mutex_lock(&_sapp.android.pt.mutex);
  8571. while (!_sapp.android.is_thread_started) {
  8572. pthread_cond_wait(&_sapp.android.pt.cond, &_sapp.android.pt.mutex);
  8573. }
  8574. pthread_mutex_unlock(&_sapp.android.pt.mutex);
  8575. /* send create msg */
  8576. pthread_mutex_lock(&_sapp.android.pt.mutex);
  8577. _sapp_android_msg(_SOKOL_ANDROID_MSG_CREATE);
  8578. while (!_sapp.android.has_created) {
  8579. pthread_cond_wait(&_sapp.android.pt.cond, &_sapp.android.pt.mutex);
  8580. }
  8581. pthread_mutex_unlock(&_sapp.android.pt.mutex);
  8582. /* register for callbacks */
  8583. activity->callbacks->onStart = _sapp_android_on_start;
  8584. activity->callbacks->onResume = _sapp_android_on_resume;
  8585. activity->callbacks->onSaveInstanceState = _sapp_android_on_save_instance_state;
  8586. activity->callbacks->onWindowFocusChanged = _sapp_android_on_window_focus_changed;
  8587. activity->callbacks->onPause = _sapp_android_on_pause;
  8588. activity->callbacks->onStop = _sapp_android_on_stop;
  8589. activity->callbacks->onDestroy = _sapp_android_on_destroy;
  8590. activity->callbacks->onNativeWindowCreated = _sapp_android_on_native_window_created;
  8591. /* activity->callbacks->onNativeWindowResized = _sapp_android_on_native_window_resized; */
  8592. /* activity->callbacks->onNativeWindowRedrawNeeded = _sapp_android_on_native_window_redraw_needed; */
  8593. activity->callbacks->onNativeWindowDestroyed = _sapp_android_on_native_window_destroyed;
  8594. activity->callbacks->onInputQueueCreated = _sapp_android_on_input_queue_created;
  8595. activity->callbacks->onInputQueueDestroyed = _sapp_android_on_input_queue_destroyed;
  8596. /* activity->callbacks->onContentRectChanged = _sapp_android_on_content_rect_changed; */
  8597. activity->callbacks->onConfigurationChanged = _sapp_android_on_config_changed;
  8598. activity->callbacks->onLowMemory = _sapp_android_on_low_memory;
  8599. SOKOL_LOG("NativeActivity successfully created");
  8600. /* NOT A BUG: do NOT call sapp_discard_state() */
  8601. }
  8602. #endif /* _SAPP_ANDROID */
  8603. /*== LINUX ==================================================================*/
  8604. #if defined(_SAPP_LINUX)
  8605. /* see GLFW's xkb_unicode.c */
  8606. static const struct _sapp_x11_codepair {
  8607. uint16_t keysym;
  8608. uint16_t ucs;
  8609. } _sapp_x11_keysymtab[] = {
  8610. { 0x01a1, 0x0104 },
  8611. { 0x01a2, 0x02d8 },
  8612. { 0x01a3, 0x0141 },
  8613. { 0x01a5, 0x013d },
  8614. { 0x01a6, 0x015a },
  8615. { 0x01a9, 0x0160 },
  8616. { 0x01aa, 0x015e },
  8617. { 0x01ab, 0x0164 },
  8618. { 0x01ac, 0x0179 },
  8619. { 0x01ae, 0x017d },
  8620. { 0x01af, 0x017b },
  8621. { 0x01b1, 0x0105 },
  8622. { 0x01b2, 0x02db },
  8623. { 0x01b3, 0x0142 },
  8624. { 0x01b5, 0x013e },
  8625. { 0x01b6, 0x015b },
  8626. { 0x01b7, 0x02c7 },
  8627. { 0x01b9, 0x0161 },
  8628. { 0x01ba, 0x015f },
  8629. { 0x01bb, 0x0165 },
  8630. { 0x01bc, 0x017a },
  8631. { 0x01bd, 0x02dd },
  8632. { 0x01be, 0x017e },
  8633. { 0x01bf, 0x017c },
  8634. { 0x01c0, 0x0154 },
  8635. { 0x01c3, 0x0102 },
  8636. { 0x01c5, 0x0139 },
  8637. { 0x01c6, 0x0106 },
  8638. { 0x01c8, 0x010c },
  8639. { 0x01ca, 0x0118 },
  8640. { 0x01cc, 0x011a },
  8641. { 0x01cf, 0x010e },
  8642. { 0x01d0, 0x0110 },
  8643. { 0x01d1, 0x0143 },
  8644. { 0x01d2, 0x0147 },
  8645. { 0x01d5, 0x0150 },
  8646. { 0x01d8, 0x0158 },
  8647. { 0x01d9, 0x016e },
  8648. { 0x01db, 0x0170 },
  8649. { 0x01de, 0x0162 },
  8650. { 0x01e0, 0x0155 },
  8651. { 0x01e3, 0x0103 },
  8652. { 0x01e5, 0x013a },
  8653. { 0x01e6, 0x0107 },
  8654. { 0x01e8, 0x010d },
  8655. { 0x01ea, 0x0119 },
  8656. { 0x01ec, 0x011b },
  8657. { 0x01ef, 0x010f },
  8658. { 0x01f0, 0x0111 },
  8659. { 0x01f1, 0x0144 },
  8660. { 0x01f2, 0x0148 },
  8661. { 0x01f5, 0x0151 },
  8662. { 0x01f8, 0x0159 },
  8663. { 0x01f9, 0x016f },
  8664. { 0x01fb, 0x0171 },
  8665. { 0x01fe, 0x0163 },
  8666. { 0x01ff, 0x02d9 },
  8667. { 0x02a1, 0x0126 },
  8668. { 0x02a6, 0x0124 },
  8669. { 0x02a9, 0x0130 },
  8670. { 0x02ab, 0x011e },
  8671. { 0x02ac, 0x0134 },
  8672. { 0x02b1, 0x0127 },
  8673. { 0x02b6, 0x0125 },
  8674. { 0x02b9, 0x0131 },
  8675. { 0x02bb, 0x011f },
  8676. { 0x02bc, 0x0135 },
  8677. { 0x02c5, 0x010a },
  8678. { 0x02c6, 0x0108 },
  8679. { 0x02d5, 0x0120 },
  8680. { 0x02d8, 0x011c },
  8681. { 0x02dd, 0x016c },
  8682. { 0x02de, 0x015c },
  8683. { 0x02e5, 0x010b },
  8684. { 0x02e6, 0x0109 },
  8685. { 0x02f5, 0x0121 },
  8686. { 0x02f8, 0x011d },
  8687. { 0x02fd, 0x016d },
  8688. { 0x02fe, 0x015d },
  8689. { 0x03a2, 0x0138 },
  8690. { 0x03a3, 0x0156 },
  8691. { 0x03a5, 0x0128 },
  8692. { 0x03a6, 0x013b },
  8693. { 0x03aa, 0x0112 },
  8694. { 0x03ab, 0x0122 },
  8695. { 0x03ac, 0x0166 },
  8696. { 0x03b3, 0x0157 },
  8697. { 0x03b5, 0x0129 },
  8698. { 0x03b6, 0x013c },
  8699. { 0x03ba, 0x0113 },
  8700. { 0x03bb, 0x0123 },
  8701. { 0x03bc, 0x0167 },
  8702. { 0x03bd, 0x014a },
  8703. { 0x03bf, 0x014b },
  8704. { 0x03c0, 0x0100 },
  8705. { 0x03c7, 0x012e },
  8706. { 0x03cc, 0x0116 },
  8707. { 0x03cf, 0x012a },
  8708. { 0x03d1, 0x0145 },
  8709. { 0x03d2, 0x014c },
  8710. { 0x03d3, 0x0136 },
  8711. { 0x03d9, 0x0172 },
  8712. { 0x03dd, 0x0168 },
  8713. { 0x03de, 0x016a },
  8714. { 0x03e0, 0x0101 },
  8715. { 0x03e7, 0x012f },
  8716. { 0x03ec, 0x0117 },
  8717. { 0x03ef, 0x012b },
  8718. { 0x03f1, 0x0146 },
  8719. { 0x03f2, 0x014d },
  8720. { 0x03f3, 0x0137 },
  8721. { 0x03f9, 0x0173 },
  8722. { 0x03fd, 0x0169 },
  8723. { 0x03fe, 0x016b },
  8724. { 0x047e, 0x203e },
  8725. { 0x04a1, 0x3002 },
  8726. { 0x04a2, 0x300c },
  8727. { 0x04a3, 0x300d },
  8728. { 0x04a4, 0x3001 },
  8729. { 0x04a5, 0x30fb },
  8730. { 0x04a6, 0x30f2 },
  8731. { 0x04a7, 0x30a1 },
  8732. { 0x04a8, 0x30a3 },
  8733. { 0x04a9, 0x30a5 },
  8734. { 0x04aa, 0x30a7 },
  8735. { 0x04ab, 0x30a9 },
  8736. { 0x04ac, 0x30e3 },
  8737. { 0x04ad, 0x30e5 },
  8738. { 0x04ae, 0x30e7 },
  8739. { 0x04af, 0x30c3 },
  8740. { 0x04b0, 0x30fc },
  8741. { 0x04b1, 0x30a2 },
  8742. { 0x04b2, 0x30a4 },
  8743. { 0x04b3, 0x30a6 },
  8744. { 0x04b4, 0x30a8 },
  8745. { 0x04b5, 0x30aa },
  8746. { 0x04b6, 0x30ab },
  8747. { 0x04b7, 0x30ad },
  8748. { 0x04b8, 0x30af },
  8749. { 0x04b9, 0x30b1 },
  8750. { 0x04ba, 0x30b3 },
  8751. { 0x04bb, 0x30b5 },
  8752. { 0x04bc, 0x30b7 },
  8753. { 0x04bd, 0x30b9 },
  8754. { 0x04be, 0x30bb },
  8755. { 0x04bf, 0x30bd },
  8756. { 0x04c0, 0x30bf },
  8757. { 0x04c1, 0x30c1 },
  8758. { 0x04c2, 0x30c4 },
  8759. { 0x04c3, 0x30c6 },
  8760. { 0x04c4, 0x30c8 },
  8761. { 0x04c5, 0x30ca },
  8762. { 0x04c6, 0x30cb },
  8763. { 0x04c7, 0x30cc },
  8764. { 0x04c8, 0x30cd },
  8765. { 0x04c9, 0x30ce },
  8766. { 0x04ca, 0x30cf },
  8767. { 0x04cb, 0x30d2 },
  8768. { 0x04cc, 0x30d5 },
  8769. { 0x04cd, 0x30d8 },
  8770. { 0x04ce, 0x30db },
  8771. { 0x04cf, 0x30de },
  8772. { 0x04d0, 0x30df },
  8773. { 0x04d1, 0x30e0 },
  8774. { 0x04d2, 0x30e1 },
  8775. { 0x04d3, 0x30e2 },
  8776. { 0x04d4, 0x30e4 },
  8777. { 0x04d5, 0x30e6 },
  8778. { 0x04d6, 0x30e8 },
  8779. { 0x04d7, 0x30e9 },
  8780. { 0x04d8, 0x30ea },
  8781. { 0x04d9, 0x30eb },
  8782. { 0x04da, 0x30ec },
  8783. { 0x04db, 0x30ed },
  8784. { 0x04dc, 0x30ef },
  8785. { 0x04dd, 0x30f3 },
  8786. { 0x04de, 0x309b },
  8787. { 0x04df, 0x309c },
  8788. { 0x05ac, 0x060c },
  8789. { 0x05bb, 0x061b },
  8790. { 0x05bf, 0x061f },
  8791. { 0x05c1, 0x0621 },
  8792. { 0x05c2, 0x0622 },
  8793. { 0x05c3, 0x0623 },
  8794. { 0x05c4, 0x0624 },
  8795. { 0x05c5, 0x0625 },
  8796. { 0x05c6, 0x0626 },
  8797. { 0x05c7, 0x0627 },
  8798. { 0x05c8, 0x0628 },
  8799. { 0x05c9, 0x0629 },
  8800. { 0x05ca, 0x062a },
  8801. { 0x05cb, 0x062b },
  8802. { 0x05cc, 0x062c },
  8803. { 0x05cd, 0x062d },
  8804. { 0x05ce, 0x062e },
  8805. { 0x05cf, 0x062f },
  8806. { 0x05d0, 0x0630 },
  8807. { 0x05d1, 0x0631 },
  8808. { 0x05d2, 0x0632 },
  8809. { 0x05d3, 0x0633 },
  8810. { 0x05d4, 0x0634 },
  8811. { 0x05d5, 0x0635 },
  8812. { 0x05d6, 0x0636 },
  8813. { 0x05d7, 0x0637 },
  8814. { 0x05d8, 0x0638 },
  8815. { 0x05d9, 0x0639 },
  8816. { 0x05da, 0x063a },
  8817. { 0x05e0, 0x0640 },
  8818. { 0x05e1, 0x0641 },
  8819. { 0x05e2, 0x0642 },
  8820. { 0x05e3, 0x0643 },
  8821. { 0x05e4, 0x0644 },
  8822. { 0x05e5, 0x0645 },
  8823. { 0x05e6, 0x0646 },
  8824. { 0x05e7, 0x0647 },
  8825. { 0x05e8, 0x0648 },
  8826. { 0x05e9, 0x0649 },
  8827. { 0x05ea, 0x064a },
  8828. { 0x05eb, 0x064b },
  8829. { 0x05ec, 0x064c },
  8830. { 0x05ed, 0x064d },
  8831. { 0x05ee, 0x064e },
  8832. { 0x05ef, 0x064f },
  8833. { 0x05f0, 0x0650 },
  8834. { 0x05f1, 0x0651 },
  8835. { 0x05f2, 0x0652 },
  8836. { 0x06a1, 0x0452 },
  8837. { 0x06a2, 0x0453 },
  8838. { 0x06a3, 0x0451 },
  8839. { 0x06a4, 0x0454 },
  8840. { 0x06a5, 0x0455 },
  8841. { 0x06a6, 0x0456 },
  8842. { 0x06a7, 0x0457 },
  8843. { 0x06a8, 0x0458 },
  8844. { 0x06a9, 0x0459 },
  8845. { 0x06aa, 0x045a },
  8846. { 0x06ab, 0x045b },
  8847. { 0x06ac, 0x045c },
  8848. { 0x06ae, 0x045e },
  8849. { 0x06af, 0x045f },
  8850. { 0x06b0, 0x2116 },
  8851. { 0x06b1, 0x0402 },
  8852. { 0x06b2, 0x0403 },
  8853. { 0x06b3, 0x0401 },
  8854. { 0x06b4, 0x0404 },
  8855. { 0x06b5, 0x0405 },
  8856. { 0x06b6, 0x0406 },
  8857. { 0x06b7, 0x0407 },
  8858. { 0x06b8, 0x0408 },
  8859. { 0x06b9, 0x0409 },
  8860. { 0x06ba, 0x040a },
  8861. { 0x06bb, 0x040b },
  8862. { 0x06bc, 0x040c },
  8863. { 0x06be, 0x040e },
  8864. { 0x06bf, 0x040f },
  8865. { 0x06c0, 0x044e },
  8866. { 0x06c1, 0x0430 },
  8867. { 0x06c2, 0x0431 },
  8868. { 0x06c3, 0x0446 },
  8869. { 0x06c4, 0x0434 },
  8870. { 0x06c5, 0x0435 },
  8871. { 0x06c6, 0x0444 },
  8872. { 0x06c7, 0x0433 },
  8873. { 0x06c8, 0x0445 },
  8874. { 0x06c9, 0x0438 },
  8875. { 0x06ca, 0x0439 },
  8876. { 0x06cb, 0x043a },
  8877. { 0x06cc, 0x043b },
  8878. { 0x06cd, 0x043c },
  8879. { 0x06ce, 0x043d },
  8880. { 0x06cf, 0x043e },
  8881. { 0x06d0, 0x043f },
  8882. { 0x06d1, 0x044f },
  8883. { 0x06d2, 0x0440 },
  8884. { 0x06d3, 0x0441 },
  8885. { 0x06d4, 0x0442 },
  8886. { 0x06d5, 0x0443 },
  8887. { 0x06d6, 0x0436 },
  8888. { 0x06d7, 0x0432 },
  8889. { 0x06d8, 0x044c },
  8890. { 0x06d9, 0x044b },
  8891. { 0x06da, 0x0437 },
  8892. { 0x06db, 0x0448 },
  8893. { 0x06dc, 0x044d },
  8894. { 0x06dd, 0x0449 },
  8895. { 0x06de, 0x0447 },
  8896. { 0x06df, 0x044a },
  8897. { 0x06e0, 0x042e },
  8898. { 0x06e1, 0x0410 },
  8899. { 0x06e2, 0x0411 },
  8900. { 0x06e3, 0x0426 },
  8901. { 0x06e4, 0x0414 },
  8902. { 0x06e5, 0x0415 },
  8903. { 0x06e6, 0x0424 },
  8904. { 0x06e7, 0x0413 },
  8905. { 0x06e8, 0x0425 },
  8906. { 0x06e9, 0x0418 },
  8907. { 0x06ea, 0x0419 },
  8908. { 0x06eb, 0x041a },
  8909. { 0x06ec, 0x041b },
  8910. { 0x06ed, 0x041c },
  8911. { 0x06ee, 0x041d },
  8912. { 0x06ef, 0x041e },
  8913. { 0x06f0, 0x041f },
  8914. { 0x06f1, 0x042f },
  8915. { 0x06f2, 0x0420 },
  8916. { 0x06f3, 0x0421 },
  8917. { 0x06f4, 0x0422 },
  8918. { 0x06f5, 0x0423 },
  8919. { 0x06f6, 0x0416 },
  8920. { 0x06f7, 0x0412 },
  8921. { 0x06f8, 0x042c },
  8922. { 0x06f9, 0x042b },
  8923. { 0x06fa, 0x0417 },
  8924. { 0x06fb, 0x0428 },
  8925. { 0x06fc, 0x042d },
  8926. { 0x06fd, 0x0429 },
  8927. { 0x06fe, 0x0427 },
  8928. { 0x06ff, 0x042a },
  8929. { 0x07a1, 0x0386 },
  8930. { 0x07a2, 0x0388 },
  8931. { 0x07a3, 0x0389 },
  8932. { 0x07a4, 0x038a },
  8933. { 0x07a5, 0x03aa },
  8934. { 0x07a7, 0x038c },
  8935. { 0x07a8, 0x038e },
  8936. { 0x07a9, 0x03ab },
  8937. { 0x07ab, 0x038f },
  8938. { 0x07ae, 0x0385 },
  8939. { 0x07af, 0x2015 },
  8940. { 0x07b1, 0x03ac },
  8941. { 0x07b2, 0x03ad },
  8942. { 0x07b3, 0x03ae },
  8943. { 0x07b4, 0x03af },
  8944. { 0x07b5, 0x03ca },
  8945. { 0x07b6, 0x0390 },
  8946. { 0x07b7, 0x03cc },
  8947. { 0x07b8, 0x03cd },
  8948. { 0x07b9, 0x03cb },
  8949. { 0x07ba, 0x03b0 },
  8950. { 0x07bb, 0x03ce },
  8951. { 0x07c1, 0x0391 },
  8952. { 0x07c2, 0x0392 },
  8953. { 0x07c3, 0x0393 },
  8954. { 0x07c4, 0x0394 },
  8955. { 0x07c5, 0x0395 },
  8956. { 0x07c6, 0x0396 },
  8957. { 0x07c7, 0x0397 },
  8958. { 0x07c8, 0x0398 },
  8959. { 0x07c9, 0x0399 },
  8960. { 0x07ca, 0x039a },
  8961. { 0x07cb, 0x039b },
  8962. { 0x07cc, 0x039c },
  8963. { 0x07cd, 0x039d },
  8964. { 0x07ce, 0x039e },
  8965. { 0x07cf, 0x039f },
  8966. { 0x07d0, 0x03a0 },
  8967. { 0x07d1, 0x03a1 },
  8968. { 0x07d2, 0x03a3 },
  8969. { 0x07d4, 0x03a4 },
  8970. { 0x07d5, 0x03a5 },
  8971. { 0x07d6, 0x03a6 },
  8972. { 0x07d7, 0x03a7 },
  8973. { 0x07d8, 0x03a8 },
  8974. { 0x07d9, 0x03a9 },
  8975. { 0x07e1, 0x03b1 },
  8976. { 0x07e2, 0x03b2 },
  8977. { 0x07e3, 0x03b3 },
  8978. { 0x07e4, 0x03b4 },
  8979. { 0x07e5, 0x03b5 },
  8980. { 0x07e6, 0x03b6 },
  8981. { 0x07e7, 0x03b7 },
  8982. { 0x07e8, 0x03b8 },
  8983. { 0x07e9, 0x03b9 },
  8984. { 0x07ea, 0x03ba },
  8985. { 0x07eb, 0x03bb },
  8986. { 0x07ec, 0x03bc },
  8987. { 0x07ed, 0x03bd },
  8988. { 0x07ee, 0x03be },
  8989. { 0x07ef, 0x03bf },
  8990. { 0x07f0, 0x03c0 },
  8991. { 0x07f1, 0x03c1 },
  8992. { 0x07f2, 0x03c3 },
  8993. { 0x07f3, 0x03c2 },
  8994. { 0x07f4, 0x03c4 },
  8995. { 0x07f5, 0x03c5 },
  8996. { 0x07f6, 0x03c6 },
  8997. { 0x07f7, 0x03c7 },
  8998. { 0x07f8, 0x03c8 },
  8999. { 0x07f9, 0x03c9 },
  9000. { 0x08a1, 0x23b7 },
  9001. { 0x08a2, 0x250c },
  9002. { 0x08a3, 0x2500 },
  9003. { 0x08a4, 0x2320 },
  9004. { 0x08a5, 0x2321 },
  9005. { 0x08a6, 0x2502 },
  9006. { 0x08a7, 0x23a1 },
  9007. { 0x08a8, 0x23a3 },
  9008. { 0x08a9, 0x23a4 },
  9009. { 0x08aa, 0x23a6 },
  9010. { 0x08ab, 0x239b },
  9011. { 0x08ac, 0x239d },
  9012. { 0x08ad, 0x239e },
  9013. { 0x08ae, 0x23a0 },
  9014. { 0x08af, 0x23a8 },
  9015. { 0x08b0, 0x23ac },
  9016. { 0x08bc, 0x2264 },
  9017. { 0x08bd, 0x2260 },
  9018. { 0x08be, 0x2265 },
  9019. { 0x08bf, 0x222b },
  9020. { 0x08c0, 0x2234 },
  9021. { 0x08c1, 0x221d },
  9022. { 0x08c2, 0x221e },
  9023. { 0x08c5, 0x2207 },
  9024. { 0x08c8, 0x223c },
  9025. { 0x08c9, 0x2243 },
  9026. { 0x08cd, 0x21d4 },
  9027. { 0x08ce, 0x21d2 },
  9028. { 0x08cf, 0x2261 },
  9029. { 0x08d6, 0x221a },
  9030. { 0x08da, 0x2282 },
  9031. { 0x08db, 0x2283 },
  9032. { 0x08dc, 0x2229 },
  9033. { 0x08dd, 0x222a },
  9034. { 0x08de, 0x2227 },
  9035. { 0x08df, 0x2228 },
  9036. { 0x08ef, 0x2202 },
  9037. { 0x08f6, 0x0192 },
  9038. { 0x08fb, 0x2190 },
  9039. { 0x08fc, 0x2191 },
  9040. { 0x08fd, 0x2192 },
  9041. { 0x08fe, 0x2193 },
  9042. { 0x09e0, 0x25c6 },
  9043. { 0x09e1, 0x2592 },
  9044. { 0x09e2, 0x2409 },
  9045. { 0x09e3, 0x240c },
  9046. { 0x09e4, 0x240d },
  9047. { 0x09e5, 0x240a },
  9048. { 0x09e8, 0x2424 },
  9049. { 0x09e9, 0x240b },
  9050. { 0x09ea, 0x2518 },
  9051. { 0x09eb, 0x2510 },
  9052. { 0x09ec, 0x250c },
  9053. { 0x09ed, 0x2514 },
  9054. { 0x09ee, 0x253c },
  9055. { 0x09ef, 0x23ba },
  9056. { 0x09f0, 0x23bb },
  9057. { 0x09f1, 0x2500 },
  9058. { 0x09f2, 0x23bc },
  9059. { 0x09f3, 0x23bd },
  9060. { 0x09f4, 0x251c },
  9061. { 0x09f5, 0x2524 },
  9062. { 0x09f6, 0x2534 },
  9063. { 0x09f7, 0x252c },
  9064. { 0x09f8, 0x2502 },
  9065. { 0x0aa1, 0x2003 },
  9066. { 0x0aa2, 0x2002 },
  9067. { 0x0aa3, 0x2004 },
  9068. { 0x0aa4, 0x2005 },
  9069. { 0x0aa5, 0x2007 },
  9070. { 0x0aa6, 0x2008 },
  9071. { 0x0aa7, 0x2009 },
  9072. { 0x0aa8, 0x200a },
  9073. { 0x0aa9, 0x2014 },
  9074. { 0x0aaa, 0x2013 },
  9075. { 0x0aae, 0x2026 },
  9076. { 0x0aaf, 0x2025 },
  9077. { 0x0ab0, 0x2153 },
  9078. { 0x0ab1, 0x2154 },
  9079. { 0x0ab2, 0x2155 },
  9080. { 0x0ab3, 0x2156 },
  9081. { 0x0ab4, 0x2157 },
  9082. { 0x0ab5, 0x2158 },
  9083. { 0x0ab6, 0x2159 },
  9084. { 0x0ab7, 0x215a },
  9085. { 0x0ab8, 0x2105 },
  9086. { 0x0abb, 0x2012 },
  9087. { 0x0abc, 0x2329 },
  9088. { 0x0abe, 0x232a },
  9089. { 0x0ac3, 0x215b },
  9090. { 0x0ac4, 0x215c },
  9091. { 0x0ac5, 0x215d },
  9092. { 0x0ac6, 0x215e },
  9093. { 0x0ac9, 0x2122 },
  9094. { 0x0aca, 0x2613 },
  9095. { 0x0acc, 0x25c1 },
  9096. { 0x0acd, 0x25b7 },
  9097. { 0x0ace, 0x25cb },
  9098. { 0x0acf, 0x25af },
  9099. { 0x0ad0, 0x2018 },
  9100. { 0x0ad1, 0x2019 },
  9101. { 0x0ad2, 0x201c },
  9102. { 0x0ad3, 0x201d },
  9103. { 0x0ad4, 0x211e },
  9104. { 0x0ad6, 0x2032 },
  9105. { 0x0ad7, 0x2033 },
  9106. { 0x0ad9, 0x271d },
  9107. { 0x0adb, 0x25ac },
  9108. { 0x0adc, 0x25c0 },
  9109. { 0x0add, 0x25b6 },
  9110. { 0x0ade, 0x25cf },
  9111. { 0x0adf, 0x25ae },
  9112. { 0x0ae0, 0x25e6 },
  9113. { 0x0ae1, 0x25ab },
  9114. { 0x0ae2, 0x25ad },
  9115. { 0x0ae3, 0x25b3 },
  9116. { 0x0ae4, 0x25bd },
  9117. { 0x0ae5, 0x2606 },
  9118. { 0x0ae6, 0x2022 },
  9119. { 0x0ae7, 0x25aa },
  9120. { 0x0ae8, 0x25b2 },
  9121. { 0x0ae9, 0x25bc },
  9122. { 0x0aea, 0x261c },
  9123. { 0x0aeb, 0x261e },
  9124. { 0x0aec, 0x2663 },
  9125. { 0x0aed, 0x2666 },
  9126. { 0x0aee, 0x2665 },
  9127. { 0x0af0, 0x2720 },
  9128. { 0x0af1, 0x2020 },
  9129. { 0x0af2, 0x2021 },
  9130. { 0x0af3, 0x2713 },
  9131. { 0x0af4, 0x2717 },
  9132. { 0x0af5, 0x266f },
  9133. { 0x0af6, 0x266d },
  9134. { 0x0af7, 0x2642 },
  9135. { 0x0af8, 0x2640 },
  9136. { 0x0af9, 0x260e },
  9137. { 0x0afa, 0x2315 },
  9138. { 0x0afb, 0x2117 },
  9139. { 0x0afc, 0x2038 },
  9140. { 0x0afd, 0x201a },
  9141. { 0x0afe, 0x201e },
  9142. { 0x0ba3, 0x003c },
  9143. { 0x0ba6, 0x003e },
  9144. { 0x0ba8, 0x2228 },
  9145. { 0x0ba9, 0x2227 },
  9146. { 0x0bc0, 0x00af },
  9147. { 0x0bc2, 0x22a5 },
  9148. { 0x0bc3, 0x2229 },
  9149. { 0x0bc4, 0x230a },
  9150. { 0x0bc6, 0x005f },
  9151. { 0x0bca, 0x2218 },
  9152. { 0x0bcc, 0x2395 },
  9153. { 0x0bce, 0x22a4 },
  9154. { 0x0bcf, 0x25cb },
  9155. { 0x0bd3, 0x2308 },
  9156. { 0x0bd6, 0x222a },
  9157. { 0x0bd8, 0x2283 },
  9158. { 0x0bda, 0x2282 },
  9159. { 0x0bdc, 0x22a2 },
  9160. { 0x0bfc, 0x22a3 },
  9161. { 0x0cdf, 0x2017 },
  9162. { 0x0ce0, 0x05d0 },
  9163. { 0x0ce1, 0x05d1 },
  9164. { 0x0ce2, 0x05d2 },
  9165. { 0x0ce3, 0x05d3 },
  9166. { 0x0ce4, 0x05d4 },
  9167. { 0x0ce5, 0x05d5 },
  9168. { 0x0ce6, 0x05d6 },
  9169. { 0x0ce7, 0x05d7 },
  9170. { 0x0ce8, 0x05d8 },
  9171. { 0x0ce9, 0x05d9 },
  9172. { 0x0cea, 0x05da },
  9173. { 0x0ceb, 0x05db },
  9174. { 0x0cec, 0x05dc },
  9175. { 0x0ced, 0x05dd },
  9176. { 0x0cee, 0x05de },
  9177. { 0x0cef, 0x05df },
  9178. { 0x0cf0, 0x05e0 },
  9179. { 0x0cf1, 0x05e1 },
  9180. { 0x0cf2, 0x05e2 },
  9181. { 0x0cf3, 0x05e3 },
  9182. { 0x0cf4, 0x05e4 },
  9183. { 0x0cf5, 0x05e5 },
  9184. { 0x0cf6, 0x05e6 },
  9185. { 0x0cf7, 0x05e7 },
  9186. { 0x0cf8, 0x05e8 },
  9187. { 0x0cf9, 0x05e9 },
  9188. { 0x0cfa, 0x05ea },
  9189. { 0x0da1, 0x0e01 },
  9190. { 0x0da2, 0x0e02 },
  9191. { 0x0da3, 0x0e03 },
  9192. { 0x0da4, 0x0e04 },
  9193. { 0x0da5, 0x0e05 },
  9194. { 0x0da6, 0x0e06 },
  9195. { 0x0da7, 0x0e07 },
  9196. { 0x0da8, 0x0e08 },
  9197. { 0x0da9, 0x0e09 },
  9198. { 0x0daa, 0x0e0a },
  9199. { 0x0dab, 0x0e0b },
  9200. { 0x0dac, 0x0e0c },
  9201. { 0x0dad, 0x0e0d },
  9202. { 0x0dae, 0x0e0e },
  9203. { 0x0daf, 0x0e0f },
  9204. { 0x0db0, 0x0e10 },
  9205. { 0x0db1, 0x0e11 },
  9206. { 0x0db2, 0x0e12 },
  9207. { 0x0db3, 0x0e13 },
  9208. { 0x0db4, 0x0e14 },
  9209. { 0x0db5, 0x0e15 },
  9210. { 0x0db6, 0x0e16 },
  9211. { 0x0db7, 0x0e17 },
  9212. { 0x0db8, 0x0e18 },
  9213. { 0x0db9, 0x0e19 },
  9214. { 0x0dba, 0x0e1a },
  9215. { 0x0dbb, 0x0e1b },
  9216. { 0x0dbc, 0x0e1c },
  9217. { 0x0dbd, 0x0e1d },
  9218. { 0x0dbe, 0x0e1e },
  9219. { 0x0dbf, 0x0e1f },
  9220. { 0x0dc0, 0x0e20 },
  9221. { 0x0dc1, 0x0e21 },
  9222. { 0x0dc2, 0x0e22 },
  9223. { 0x0dc3, 0x0e23 },
  9224. { 0x0dc4, 0x0e24 },
  9225. { 0x0dc5, 0x0e25 },
  9226. { 0x0dc6, 0x0e26 },
  9227. { 0x0dc7, 0x0e27 },
  9228. { 0x0dc8, 0x0e28 },
  9229. { 0x0dc9, 0x0e29 },
  9230. { 0x0dca, 0x0e2a },
  9231. { 0x0dcb, 0x0e2b },
  9232. { 0x0dcc, 0x0e2c },
  9233. { 0x0dcd, 0x0e2d },
  9234. { 0x0dce, 0x0e2e },
  9235. { 0x0dcf, 0x0e2f },
  9236. { 0x0dd0, 0x0e30 },
  9237. { 0x0dd1, 0x0e31 },
  9238. { 0x0dd2, 0x0e32 },
  9239. { 0x0dd3, 0x0e33 },
  9240. { 0x0dd4, 0x0e34 },
  9241. { 0x0dd5, 0x0e35 },
  9242. { 0x0dd6, 0x0e36 },
  9243. { 0x0dd7, 0x0e37 },
  9244. { 0x0dd8, 0x0e38 },
  9245. { 0x0dd9, 0x0e39 },
  9246. { 0x0dda, 0x0e3a },
  9247. { 0x0ddf, 0x0e3f },
  9248. { 0x0de0, 0x0e40 },
  9249. { 0x0de1, 0x0e41 },
  9250. { 0x0de2, 0x0e42 },
  9251. { 0x0de3, 0x0e43 },
  9252. { 0x0de4, 0x0e44 },
  9253. { 0x0de5, 0x0e45 },
  9254. { 0x0de6, 0x0e46 },
  9255. { 0x0de7, 0x0e47 },
  9256. { 0x0de8, 0x0e48 },
  9257. { 0x0de9, 0x0e49 },
  9258. { 0x0dea, 0x0e4a },
  9259. { 0x0deb, 0x0e4b },
  9260. { 0x0dec, 0x0e4c },
  9261. { 0x0ded, 0x0e4d },
  9262. { 0x0df0, 0x0e50 },
  9263. { 0x0df1, 0x0e51 },
  9264. { 0x0df2, 0x0e52 },
  9265. { 0x0df3, 0x0e53 },
  9266. { 0x0df4, 0x0e54 },
  9267. { 0x0df5, 0x0e55 },
  9268. { 0x0df6, 0x0e56 },
  9269. { 0x0df7, 0x0e57 },
  9270. { 0x0df8, 0x0e58 },
  9271. { 0x0df9, 0x0e59 },
  9272. { 0x0ea1, 0x3131 },
  9273. { 0x0ea2, 0x3132 },
  9274. { 0x0ea3, 0x3133 },
  9275. { 0x0ea4, 0x3134 },
  9276. { 0x0ea5, 0x3135 },
  9277. { 0x0ea6, 0x3136 },
  9278. { 0x0ea7, 0x3137 },
  9279. { 0x0ea8, 0x3138 },
  9280. { 0x0ea9, 0x3139 },
  9281. { 0x0eaa, 0x313a },
  9282. { 0x0eab, 0x313b },
  9283. { 0x0eac, 0x313c },
  9284. { 0x0ead, 0x313d },
  9285. { 0x0eae, 0x313e },
  9286. { 0x0eaf, 0x313f },
  9287. { 0x0eb0, 0x3140 },
  9288. { 0x0eb1, 0x3141 },
  9289. { 0x0eb2, 0x3142 },
  9290. { 0x0eb3, 0x3143 },
  9291. { 0x0eb4, 0x3144 },
  9292. { 0x0eb5, 0x3145 },
  9293. { 0x0eb6, 0x3146 },
  9294. { 0x0eb7, 0x3147 },
  9295. { 0x0eb8, 0x3148 },
  9296. { 0x0eb9, 0x3149 },
  9297. { 0x0eba, 0x314a },
  9298. { 0x0ebb, 0x314b },
  9299. { 0x0ebc, 0x314c },
  9300. { 0x0ebd, 0x314d },
  9301. { 0x0ebe, 0x314e },
  9302. { 0x0ebf, 0x314f },
  9303. { 0x0ec0, 0x3150 },
  9304. { 0x0ec1, 0x3151 },
  9305. { 0x0ec2, 0x3152 },
  9306. { 0x0ec3, 0x3153 },
  9307. { 0x0ec4, 0x3154 },
  9308. { 0x0ec5, 0x3155 },
  9309. { 0x0ec6, 0x3156 },
  9310. { 0x0ec7, 0x3157 },
  9311. { 0x0ec8, 0x3158 },
  9312. { 0x0ec9, 0x3159 },
  9313. { 0x0eca, 0x315a },
  9314. { 0x0ecb, 0x315b },
  9315. { 0x0ecc, 0x315c },
  9316. { 0x0ecd, 0x315d },
  9317. { 0x0ece, 0x315e },
  9318. { 0x0ecf, 0x315f },
  9319. { 0x0ed0, 0x3160 },
  9320. { 0x0ed1, 0x3161 },
  9321. { 0x0ed2, 0x3162 },
  9322. { 0x0ed3, 0x3163 },
  9323. { 0x0ed4, 0x11a8 },
  9324. { 0x0ed5, 0x11a9 },
  9325. { 0x0ed6, 0x11aa },
  9326. { 0x0ed7, 0x11ab },
  9327. { 0x0ed8, 0x11ac },
  9328. { 0x0ed9, 0x11ad },
  9329. { 0x0eda, 0x11ae },
  9330. { 0x0edb, 0x11af },
  9331. { 0x0edc, 0x11b0 },
  9332. { 0x0edd, 0x11b1 },
  9333. { 0x0ede, 0x11b2 },
  9334. { 0x0edf, 0x11b3 },
  9335. { 0x0ee0, 0x11b4 },
  9336. { 0x0ee1, 0x11b5 },
  9337. { 0x0ee2, 0x11b6 },
  9338. { 0x0ee3, 0x11b7 },
  9339. { 0x0ee4, 0x11b8 },
  9340. { 0x0ee5, 0x11b9 },
  9341. { 0x0ee6, 0x11ba },
  9342. { 0x0ee7, 0x11bb },
  9343. { 0x0ee8, 0x11bc },
  9344. { 0x0ee9, 0x11bd },
  9345. { 0x0eea, 0x11be },
  9346. { 0x0eeb, 0x11bf },
  9347. { 0x0eec, 0x11c0 },
  9348. { 0x0eed, 0x11c1 },
  9349. { 0x0eee, 0x11c2 },
  9350. { 0x0eef, 0x316d },
  9351. { 0x0ef0, 0x3171 },
  9352. { 0x0ef1, 0x3178 },
  9353. { 0x0ef2, 0x317f },
  9354. { 0x0ef3, 0x3181 },
  9355. { 0x0ef4, 0x3184 },
  9356. { 0x0ef5, 0x3186 },
  9357. { 0x0ef6, 0x318d },
  9358. { 0x0ef7, 0x318e },
  9359. { 0x0ef8, 0x11eb },
  9360. { 0x0ef9, 0x11f0 },
  9361. { 0x0efa, 0x11f9 },
  9362. { 0x0eff, 0x20a9 },
  9363. { 0x13a4, 0x20ac },
  9364. { 0x13bc, 0x0152 },
  9365. { 0x13bd, 0x0153 },
  9366. { 0x13be, 0x0178 },
  9367. { 0x20ac, 0x20ac },
  9368. { 0xfe50, '`' },
  9369. { 0xfe51, 0x00b4 },
  9370. { 0xfe52, '^' },
  9371. { 0xfe53, '~' },
  9372. { 0xfe54, 0x00af },
  9373. { 0xfe55, 0x02d8 },
  9374. { 0xfe56, 0x02d9 },
  9375. { 0xfe57, 0x00a8 },
  9376. { 0xfe58, 0x02da },
  9377. { 0xfe59, 0x02dd },
  9378. { 0xfe5a, 0x02c7 },
  9379. { 0xfe5b, 0x00b8 },
  9380. { 0xfe5c, 0x02db },
  9381. { 0xfe5d, 0x037a },
  9382. { 0xfe5e, 0x309b },
  9383. { 0xfe5f, 0x309c },
  9384. { 0xfe63, '/' },
  9385. { 0xfe64, 0x02bc },
  9386. { 0xfe65, 0x02bd },
  9387. { 0xfe66, 0x02f5 },
  9388. { 0xfe67, 0x02f3 },
  9389. { 0xfe68, 0x02cd },
  9390. { 0xfe69, 0xa788 },
  9391. { 0xfe6a, 0x02f7 },
  9392. { 0xfe6e, ',' },
  9393. { 0xfe6f, 0x00a4 },
  9394. { 0xfe80, 'a' }, /* XK_dead_a */
  9395. { 0xfe81, 'A' }, /* XK_dead_A */
  9396. { 0xfe82, 'e' }, /* XK_dead_e */
  9397. { 0xfe83, 'E' }, /* XK_dead_E */
  9398. { 0xfe84, 'i' }, /* XK_dead_i */
  9399. { 0xfe85, 'I' }, /* XK_dead_I */
  9400. { 0xfe86, 'o' }, /* XK_dead_o */
  9401. { 0xfe87, 'O' }, /* XK_dead_O */
  9402. { 0xfe88, 'u' }, /* XK_dead_u */
  9403. { 0xfe89, 'U' }, /* XK_dead_U */
  9404. { 0xfe8a, 0x0259 },
  9405. { 0xfe8b, 0x018f },
  9406. { 0xfe8c, 0x00b5 },
  9407. { 0xfe90, '_' },
  9408. { 0xfe91, 0x02c8 },
  9409. { 0xfe92, 0x02cc },
  9410. { 0xff80 /*XKB_KEY_KP_Space*/, ' ' },
  9411. { 0xff95 /*XKB_KEY_KP_7*/, 0x0037 },
  9412. { 0xff96 /*XKB_KEY_KP_4*/, 0x0034 },
  9413. { 0xff97 /*XKB_KEY_KP_8*/, 0x0038 },
  9414. { 0xff98 /*XKB_KEY_KP_6*/, 0x0036 },
  9415. { 0xff99 /*XKB_KEY_KP_2*/, 0x0032 },
  9416. { 0xff9a /*XKB_KEY_KP_9*/, 0x0039 },
  9417. { 0xff9b /*XKB_KEY_KP_3*/, 0x0033 },
  9418. { 0xff9c /*XKB_KEY_KP_1*/, 0x0031 },
  9419. { 0xff9d /*XKB_KEY_KP_5*/, 0x0035 },
  9420. { 0xff9e /*XKB_KEY_KP_0*/, 0x0030 },
  9421. { 0xffaa /*XKB_KEY_KP_Multiply*/, '*' },
  9422. { 0xffab /*XKB_KEY_KP_Add*/, '+' },
  9423. { 0xffac /*XKB_KEY_KP_Separator*/, ',' },
  9424. { 0xffad /*XKB_KEY_KP_Subtract*/, '-' },
  9425. { 0xffae /*XKB_KEY_KP_Decimal*/, '.' },
  9426. { 0xffaf /*XKB_KEY_KP_Divide*/, '/' },
  9427. { 0xffb0 /*XKB_KEY_KP_0*/, 0x0030 },
  9428. { 0xffb1 /*XKB_KEY_KP_1*/, 0x0031 },
  9429. { 0xffb2 /*XKB_KEY_KP_2*/, 0x0032 },
  9430. { 0xffb3 /*XKB_KEY_KP_3*/, 0x0033 },
  9431. { 0xffb4 /*XKB_KEY_KP_4*/, 0x0034 },
  9432. { 0xffb5 /*XKB_KEY_KP_5*/, 0x0035 },
  9433. { 0xffb6 /*XKB_KEY_KP_6*/, 0x0036 },
  9434. { 0xffb7 /*XKB_KEY_KP_7*/, 0x0037 },
  9435. { 0xffb8 /*XKB_KEY_KP_8*/, 0x0038 },
  9436. { 0xffb9 /*XKB_KEY_KP_9*/, 0x0039 },
  9437. { 0xffbd /*XKB_KEY_KP_Equal*/, '=' }
  9438. };
  9439. _SOKOL_PRIVATE int _sapp_x11_error_handler(Display* display, XErrorEvent* event) {
  9440. _SOKOL_UNUSED(display);
  9441. _sapp.x11.error_code = event->error_code;
  9442. return 0;
  9443. }
  9444. _SOKOL_PRIVATE void _sapp_x11_grab_error_handler(void) {
  9445. _sapp.x11.error_code = Success;
  9446. XSetErrorHandler(_sapp_x11_error_handler);
  9447. }
  9448. _SOKOL_PRIVATE void _sapp_x11_release_error_handler(void) {
  9449. XSync(_sapp.x11.display, False);
  9450. XSetErrorHandler(NULL);
  9451. }
  9452. _SOKOL_PRIVATE void _sapp_x11_init_extensions(void) {
  9453. _sapp.x11.UTF8_STRING = XInternAtom(_sapp.x11.display, "UTF8_STRING", False);
  9454. _sapp.x11.WM_PROTOCOLS = XInternAtom(_sapp.x11.display, "WM_PROTOCOLS", False);
  9455. _sapp.x11.WM_DELETE_WINDOW = XInternAtom(_sapp.x11.display, "WM_DELETE_WINDOW", False);
  9456. _sapp.x11.WM_STATE = XInternAtom(_sapp.x11.display, "WM_STATE", False);
  9457. _sapp.x11.NET_WM_NAME = XInternAtom(_sapp.x11.display, "_NET_WM_NAME", False);
  9458. _sapp.x11.NET_WM_ICON_NAME = XInternAtom(_sapp.x11.display, "_NET_WM_ICON_NAME", False);
  9459. _sapp.x11.NET_WM_ICON = XInternAtom(_sapp.x11.display, "_NET_WM_ICON", False);
  9460. _sapp.x11.NET_WM_STATE = XInternAtom(_sapp.x11.display, "_NET_WM_STATE", False);
  9461. _sapp.x11.NET_WM_STATE_FULLSCREEN = XInternAtom(_sapp.x11.display, "_NET_WM_STATE_FULLSCREEN", False);
  9462. if (_sapp.drop.enabled) {
  9463. _sapp.x11.xdnd.XdndAware = XInternAtom(_sapp.x11.display, "XdndAware", False);
  9464. _sapp.x11.xdnd.XdndEnter = XInternAtom(_sapp.x11.display, "XdndEnter", False);
  9465. _sapp.x11.xdnd.XdndPosition = XInternAtom(_sapp.x11.display, "XdndPosition", False);
  9466. _sapp.x11.xdnd.XdndStatus = XInternAtom(_sapp.x11.display, "XdndStatus", False);
  9467. _sapp.x11.xdnd.XdndActionCopy = XInternAtom(_sapp.x11.display, "XdndActionCopy", False);
  9468. _sapp.x11.xdnd.XdndDrop = XInternAtom(_sapp.x11.display, "XdndDrop", False);
  9469. _sapp.x11.xdnd.XdndFinished = XInternAtom(_sapp.x11.display, "XdndFinished", False);
  9470. _sapp.x11.xdnd.XdndSelection = XInternAtom(_sapp.x11.display, "XdndSelection", False);
  9471. _sapp.x11.xdnd.XdndTypeList = XInternAtom(_sapp.x11.display, "XdndTypeList", False);
  9472. _sapp.x11.xdnd.text_uri_list = XInternAtom(_sapp.x11.display, "text/uri-list", False);
  9473. }
  9474. /* check Xi extension for raw mouse input */
  9475. if (XQueryExtension(_sapp.x11.display, "XInputExtension", &_sapp.x11.xi.major_opcode, &_sapp.x11.xi.event_base, &_sapp.x11.xi.error_base)) {
  9476. _sapp.x11.xi.major = 2;
  9477. _sapp.x11.xi.minor = 0;
  9478. if (XIQueryVersion(_sapp.x11.display, &_sapp.x11.xi.major, &_sapp.x11.xi.minor) == Success) {
  9479. _sapp.x11.xi.available = true;
  9480. }
  9481. }
  9482. }
  9483. _SOKOL_PRIVATE void _sapp_x11_query_system_dpi(void) {
  9484. /* from GLFW:
  9485. NOTE: Default to the display-wide DPI as we don't currently have a policy
  9486. for which monitor a window is considered to be on
  9487. _sapp.x11.dpi = DisplayWidth(_sapp.x11.display, _sapp.x11.screen) *
  9488. 25.4f / DisplayWidthMM(_sapp.x11.display, _sapp.x11.screen);
  9489. NOTE: Basing the scale on Xft.dpi where available should provide the most
  9490. consistent user experience (matches Qt, Gtk, etc), although not
  9491. always the most accurate one
  9492. */
  9493. char* rms = XResourceManagerString(_sapp.x11.display);
  9494. if (rms) {
  9495. XrmDatabase db = XrmGetStringDatabase(rms);
  9496. if (db) {
  9497. XrmValue value;
  9498. char* type = NULL;
  9499. if (XrmGetResource(db, "Xft.dpi", "Xft.Dpi", &type, &value)) {
  9500. if (type && strcmp(type, "String") == 0) {
  9501. _sapp.x11.dpi = atof(value.addr);
  9502. }
  9503. }
  9504. XrmDestroyDatabase(db);
  9505. }
  9506. }
  9507. }
  9508. _SOKOL_PRIVATE bool _sapp_glx_has_ext(const char* ext, const char* extensions) {
  9509. SOKOL_ASSERT(ext);
  9510. const char* start = extensions;
  9511. while (true) {
  9512. const char* where = strstr(start, ext);
  9513. if (!where) {
  9514. return false;
  9515. }
  9516. const char* terminator = where + strlen(ext);
  9517. if ((where == start) || (*(where - 1) == ' ')) {
  9518. if (*terminator == ' ' || *terminator == '\0') {
  9519. break;
  9520. }
  9521. }
  9522. start = terminator;
  9523. }
  9524. return true;
  9525. }
  9526. _SOKOL_PRIVATE bool _sapp_glx_extsupported(const char* ext, const char* extensions) {
  9527. if (extensions) {
  9528. return _sapp_glx_has_ext(ext, extensions);
  9529. }
  9530. else {
  9531. return false;
  9532. }
  9533. }
  9534. _SOKOL_PRIVATE void* _sapp_glx_getprocaddr(const char* procname)
  9535. {
  9536. if (_sapp.glx.GetProcAddress) {
  9537. return (void*) _sapp.glx.GetProcAddress(procname);
  9538. }
  9539. else if (_sapp.glx.GetProcAddressARB) {
  9540. return (void*) _sapp.glx.GetProcAddressARB(procname);
  9541. }
  9542. else {
  9543. return dlsym(_sapp.glx.libgl, procname);
  9544. }
  9545. }
  9546. _SOKOL_PRIVATE void _sapp_glx_init() {
  9547. const char* sonames[] = { "libGL.so.1", "libGL.so", 0 };
  9548. for (int i = 0; sonames[i]; i++) {
  9549. _sapp.glx.libgl = dlopen(sonames[i], RTLD_LAZY|RTLD_GLOBAL);
  9550. if (_sapp.glx.libgl) {
  9551. break;
  9552. }
  9553. }
  9554. if (!_sapp.glx.libgl) {
  9555. _sapp_fail("GLX: failed to load libGL");
  9556. }
  9557. _sapp.glx.GetFBConfigs = (PFNGLXGETFBCONFIGSPROC) dlsym(_sapp.glx.libgl, "glXGetFBConfigs");
  9558. _sapp.glx.GetFBConfigAttrib = (PFNGLXGETFBCONFIGATTRIBPROC) dlsym(_sapp.glx.libgl, "glXGetFBConfigAttrib");
  9559. _sapp.glx.GetClientString = (PFNGLXGETCLIENTSTRINGPROC) dlsym(_sapp.glx.libgl, "glXGetClientString");
  9560. _sapp.glx.QueryExtension = (PFNGLXQUERYEXTENSIONPROC) dlsym(_sapp.glx.libgl, "glXQueryExtension");
  9561. _sapp.glx.QueryVersion = (PFNGLXQUERYVERSIONPROC) dlsym(_sapp.glx.libgl, "glXQueryVersion");
  9562. _sapp.glx.DestroyContext = (PFNGLXDESTROYCONTEXTPROC) dlsym(_sapp.glx.libgl, "glXDestroyContext");
  9563. _sapp.glx.MakeCurrent = (PFNGLXMAKECURRENTPROC) dlsym(_sapp.glx.libgl, "glXMakeCurrent");
  9564. _sapp.glx.SwapBuffers = (PFNGLXSWAPBUFFERSPROC) dlsym(_sapp.glx.libgl, "glXSwapBuffers");
  9565. _sapp.glx.QueryExtensionsString = (PFNGLXQUERYEXTENSIONSSTRINGPROC) dlsym(_sapp.glx.libgl, "glXQueryExtensionsString");
  9566. _sapp.glx.CreateWindow = (PFNGLXCREATEWINDOWPROC) dlsym(_sapp.glx.libgl, "glXCreateWindow");
  9567. _sapp.glx.DestroyWindow = (PFNGLXDESTROYWINDOWPROC) dlsym(_sapp.glx.libgl, "glXDestroyWindow");
  9568. _sapp.glx.GetProcAddress = (PFNGLXGETPROCADDRESSPROC) dlsym(_sapp.glx.libgl, "glXGetProcAddress");
  9569. _sapp.glx.GetProcAddressARB = (PFNGLXGETPROCADDRESSPROC) dlsym(_sapp.glx.libgl, "glXGetProcAddressARB");
  9570. _sapp.glx.GetVisualFromFBConfig = (PFNGLXGETVISUALFROMFBCONFIGPROC) dlsym(_sapp.glx.libgl, "glXGetVisualFromFBConfig");
  9571. if (!_sapp.glx.GetFBConfigs ||
  9572. !_sapp.glx.GetFBConfigAttrib ||
  9573. !_sapp.glx.GetClientString ||
  9574. !_sapp.glx.QueryExtension ||
  9575. !_sapp.glx.QueryVersion ||
  9576. !_sapp.glx.DestroyContext ||
  9577. !_sapp.glx.MakeCurrent ||
  9578. !_sapp.glx.SwapBuffers ||
  9579. !_sapp.glx.QueryExtensionsString ||
  9580. !_sapp.glx.CreateWindow ||
  9581. !_sapp.glx.DestroyWindow ||
  9582. !_sapp.glx.GetProcAddress ||
  9583. !_sapp.glx.GetProcAddressARB ||
  9584. !_sapp.glx.GetVisualFromFBConfig)
  9585. {
  9586. _sapp_fail("GLX: failed to load required entry points");
  9587. }
  9588. if (!_sapp.glx.QueryExtension(_sapp.x11.display, &_sapp.glx.error_base, &_sapp.glx.event_base)) {
  9589. _sapp_fail("GLX: GLX extension not found");
  9590. }
  9591. if (!_sapp.glx.QueryVersion(_sapp.x11.display, &_sapp.glx.major, &_sapp.glx.minor)) {
  9592. _sapp_fail("GLX: Failed to query GLX version");
  9593. }
  9594. if (_sapp.glx.major == 1 && _sapp.glx.minor < 3) {
  9595. _sapp_fail("GLX: GLX version 1.3 is required");
  9596. }
  9597. const char* exts = _sapp.glx.QueryExtensionsString(_sapp.x11.display, _sapp.x11.screen);
  9598. if (_sapp_glx_extsupported("GLX_EXT_swap_control", exts)) {
  9599. _sapp.glx.SwapIntervalEXT = (PFNGLXSWAPINTERVALEXTPROC) _sapp_glx_getprocaddr("glXSwapIntervalEXT");
  9600. _sapp.glx.EXT_swap_control = 0 != _sapp.glx.SwapIntervalEXT;
  9601. }
  9602. if (_sapp_glx_extsupported("GLX_MESA_swap_control", exts)) {
  9603. _sapp.glx.SwapIntervalMESA = (PFNGLXSWAPINTERVALMESAPROC) _sapp_glx_getprocaddr("glXSwapIntervalMESA");
  9604. _sapp.glx.MESA_swap_control = 0 != _sapp.glx.SwapIntervalMESA;
  9605. }
  9606. _sapp.glx.ARB_multisample = _sapp_glx_extsupported("GLX_ARB_multisample", exts);
  9607. if (_sapp_glx_extsupported("GLX_ARB_create_context", exts)) {
  9608. _sapp.glx.CreateContextAttribsARB = (PFNGLXCREATECONTEXTATTRIBSARBPROC) _sapp_glx_getprocaddr("glXCreateContextAttribsARB");
  9609. _sapp.glx.ARB_create_context = 0 != _sapp.glx.CreateContextAttribsARB;
  9610. }
  9611. _sapp.glx.ARB_create_context_profile = _sapp_glx_extsupported("GLX_ARB_create_context_profile", exts);
  9612. }
  9613. _SOKOL_PRIVATE int _sapp_glx_attrib(GLXFBConfig fbconfig, int attrib) {
  9614. int value;
  9615. _sapp.glx.GetFBConfigAttrib(_sapp.x11.display, fbconfig, attrib, &value);
  9616. return value;
  9617. }
  9618. _SOKOL_PRIVATE GLXFBConfig _sapp_glx_choosefbconfig() {
  9619. GLXFBConfig* native_configs;
  9620. _sapp_gl_fbconfig* usable_configs;
  9621. const _sapp_gl_fbconfig* closest;
  9622. int i, native_count, usable_count;
  9623. const char* vendor;
  9624. bool trust_window_bit = true;
  9625. /* HACK: This is a (hopefully temporary) workaround for Chromium
  9626. (VirtualBox GL) not setting the window bit on any GLXFBConfigs
  9627. */
  9628. vendor = _sapp.glx.GetClientString(_sapp.x11.display, GLX_VENDOR);
  9629. if (vendor && strcmp(vendor, "Chromium") == 0) {
  9630. trust_window_bit = false;
  9631. }
  9632. native_configs = _sapp.glx.GetFBConfigs(_sapp.x11.display, _sapp.x11.screen, &native_count);
  9633. if (!native_configs || !native_count) {
  9634. _sapp_fail("GLX: No GLXFBConfigs returned");
  9635. }
  9636. usable_configs = (_sapp_gl_fbconfig*) SOKOL_CALLOC((size_t)native_count, sizeof(_sapp_gl_fbconfig));
  9637. usable_count = 0;
  9638. for (i = 0; i < native_count; i++) {
  9639. const GLXFBConfig n = native_configs[i];
  9640. _sapp_gl_fbconfig* u = usable_configs + usable_count;
  9641. _sapp_gl_init_fbconfig(u);
  9642. /* Only consider RGBA GLXFBConfigs */
  9643. if (0 == (_sapp_glx_attrib(n, GLX_RENDER_TYPE) & GLX_RGBA_BIT)) {
  9644. continue;
  9645. }
  9646. /* Only consider window GLXFBConfigs */
  9647. if (0 == (_sapp_glx_attrib(n, GLX_DRAWABLE_TYPE) & GLX_WINDOW_BIT)) {
  9648. if (trust_window_bit) {
  9649. continue;
  9650. }
  9651. }
  9652. u->red_bits = _sapp_glx_attrib(n, GLX_RED_SIZE);
  9653. u->green_bits = _sapp_glx_attrib(n, GLX_GREEN_SIZE);
  9654. u->blue_bits = _sapp_glx_attrib(n, GLX_BLUE_SIZE);
  9655. u->alpha_bits = _sapp_glx_attrib(n, GLX_ALPHA_SIZE);
  9656. u->depth_bits = _sapp_glx_attrib(n, GLX_DEPTH_SIZE);
  9657. u->stencil_bits = _sapp_glx_attrib(n, GLX_STENCIL_SIZE);
  9658. if (_sapp_glx_attrib(n, GLX_DOUBLEBUFFER)) {
  9659. u->doublebuffer = true;
  9660. }
  9661. if (_sapp.glx.ARB_multisample) {
  9662. u->samples = _sapp_glx_attrib(n, GLX_SAMPLES);
  9663. }
  9664. u->handle = (uintptr_t) n;
  9665. usable_count++;
  9666. }
  9667. _sapp_gl_fbconfig desired;
  9668. _sapp_gl_init_fbconfig(&desired);
  9669. desired.red_bits = 8;
  9670. desired.green_bits = 8;
  9671. desired.blue_bits = 8;
  9672. desired.alpha_bits = 8;
  9673. desired.depth_bits = 24;
  9674. desired.stencil_bits = 8;
  9675. desired.doublebuffer = true;
  9676. desired.samples = _sapp.sample_count > 1 ? _sapp.sample_count : 0;
  9677. closest = _sapp_gl_choose_fbconfig(&desired, usable_configs, usable_count);
  9678. GLXFBConfig result = 0;
  9679. if (closest) {
  9680. result = (GLXFBConfig) closest->handle;
  9681. }
  9682. XFree(native_configs);
  9683. SOKOL_FREE(usable_configs);
  9684. return result;
  9685. }
  9686. _SOKOL_PRIVATE void _sapp_glx_choose_visual(Visual** visual, int* depth) {
  9687. GLXFBConfig native = _sapp_glx_choosefbconfig();
  9688. if (0 == native) {
  9689. _sapp_fail("GLX: Failed to find a suitable GLXFBConfig");
  9690. }
  9691. XVisualInfo* result = _sapp.glx.GetVisualFromFBConfig(_sapp.x11.display, native);
  9692. if (!result) {
  9693. _sapp_fail("GLX: Failed to retrieve Visual for GLXFBConfig");
  9694. }
  9695. *visual = result->visual;
  9696. *depth = result->depth;
  9697. XFree(result);
  9698. }
  9699. _SOKOL_PRIVATE void _sapp_glx_create_context(void) {
  9700. GLXFBConfig native = _sapp_glx_choosefbconfig();
  9701. if (0 == native){
  9702. _sapp_fail("GLX: Failed to find a suitable GLXFBConfig (2)");
  9703. }
  9704. if (!(_sapp.glx.ARB_create_context && _sapp.glx.ARB_create_context_profile)) {
  9705. _sapp_fail("GLX: ARB_create_context and ARB_create_context_profile required");
  9706. }
  9707. _sapp_x11_grab_error_handler();
  9708. const int attribs[] = {
  9709. GLX_CONTEXT_MAJOR_VERSION_ARB, 3,
  9710. GLX_CONTEXT_MINOR_VERSION_ARB, 3,
  9711. GLX_CONTEXT_PROFILE_MASK_ARB, GLX_CONTEXT_CORE_PROFILE_BIT_ARB,
  9712. GLX_CONTEXT_FLAGS_ARB, GLX_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB,
  9713. 0, 0
  9714. };
  9715. _sapp.glx.ctx = _sapp.glx.CreateContextAttribsARB(_sapp.x11.display, native, NULL, True, attribs);
  9716. if (!_sapp.glx.ctx) {
  9717. _sapp_fail("GLX: failed to create GL context");
  9718. }
  9719. _sapp_x11_release_error_handler();
  9720. _sapp.glx.window = _sapp.glx.CreateWindow(_sapp.x11.display, native, _sapp.x11.window, NULL);
  9721. if (!_sapp.glx.window) {
  9722. _sapp_fail("GLX: failed to create window");
  9723. }
  9724. }
  9725. _SOKOL_PRIVATE void _sapp_glx_destroy_context(void) {
  9726. if (_sapp.glx.window) {
  9727. _sapp.glx.DestroyWindow(_sapp.x11.display, _sapp.glx.window);
  9728. _sapp.glx.window = 0;
  9729. }
  9730. if (_sapp.glx.ctx) {
  9731. _sapp.glx.DestroyContext(_sapp.x11.display, _sapp.glx.ctx);
  9732. _sapp.glx.ctx = 0;
  9733. }
  9734. }
  9735. _SOKOL_PRIVATE void _sapp_glx_make_current(void) {
  9736. _sapp.glx.MakeCurrent(_sapp.x11.display, _sapp.glx.window, _sapp.glx.ctx);
  9737. }
  9738. _SOKOL_PRIVATE void _sapp_glx_swap_buffers(void) {
  9739. _sapp.glx.SwapBuffers(_sapp.x11.display, _sapp.glx.window);
  9740. }
  9741. _SOKOL_PRIVATE void _sapp_glx_swapinterval(int interval) {
  9742. _sapp_glx_make_current();
  9743. if (_sapp.glx.EXT_swap_control) {
  9744. _sapp.glx.SwapIntervalEXT(_sapp.x11.display, _sapp.glx.window, interval);
  9745. }
  9746. else if (_sapp.glx.MESA_swap_control) {
  9747. _sapp.glx.SwapIntervalMESA(interval);
  9748. }
  9749. }
  9750. _SOKOL_PRIVATE void _sapp_x11_send_event(Atom type, int a, int b, int c, int d, int e) {
  9751. XEvent event;
  9752. memset(&event, 0, sizeof(event));
  9753. event.type = ClientMessage;
  9754. event.xclient.window = _sapp.x11.window;
  9755. event.xclient.format = 32;
  9756. event.xclient.message_type = type;
  9757. event.xclient.data.l[0] = a;
  9758. event.xclient.data.l[1] = b;
  9759. event.xclient.data.l[2] = c;
  9760. event.xclient.data.l[3] = d;
  9761. event.xclient.data.l[4] = e;
  9762. XSendEvent(_sapp.x11.display, _sapp.x11.root,
  9763. False,
  9764. SubstructureNotifyMask | SubstructureRedirectMask,
  9765. &event);
  9766. }
  9767. _SOKOL_PRIVATE void _sapp_x11_query_window_size(void) {
  9768. XWindowAttributes attribs;
  9769. XGetWindowAttributes(_sapp.x11.display, _sapp.x11.window, &attribs);
  9770. _sapp.window_width = attribs.width;
  9771. _sapp.window_height = attribs.height;
  9772. _sapp.framebuffer_width = _sapp.window_width;
  9773. _sapp.framebuffer_height = _sapp.window_height;
  9774. }
  9775. _SOKOL_PRIVATE void _sapp_x11_set_fullscreen(bool enable) {
  9776. /* NOTE: this function must be called after XMapWindow (which happens in _sapp_x11_show_window()) */
  9777. if (_sapp.x11.NET_WM_STATE && _sapp.x11.NET_WM_STATE_FULLSCREEN) {
  9778. if (enable) {
  9779. const int _NET_WM_STATE_ADD = 1;
  9780. _sapp_x11_send_event(_sapp.x11.NET_WM_STATE,
  9781. _NET_WM_STATE_ADD,
  9782. _sapp.x11.NET_WM_STATE_FULLSCREEN,
  9783. 0, 1, 0);
  9784. }
  9785. else {
  9786. const int _NET_WM_STATE_REMOVE = 0;
  9787. _sapp_x11_send_event(_sapp.x11.NET_WM_STATE,
  9788. _NET_WM_STATE_REMOVE,
  9789. _sapp.x11.NET_WM_STATE_FULLSCREEN,
  9790. 0, 1, 0);
  9791. }
  9792. }
  9793. XFlush(_sapp.x11.display);
  9794. }
  9795. _SOKOL_PRIVATE void _sapp_x11_create_hidden_cursor(void) {
  9796. SOKOL_ASSERT(0 == _sapp.x11.hidden_cursor);
  9797. const int w = 16;
  9798. const int h = 16;
  9799. XcursorImage* img = XcursorImageCreate(w, h);
  9800. SOKOL_ASSERT(img && (img->width == 16) && (img->height == 16) && img->pixels);
  9801. img->xhot = 0;
  9802. img->yhot = 0;
  9803. const size_t num_bytes = (size_t)(w * h) * sizeof(XcursorPixel);
  9804. memset(img->pixels, 0, num_bytes);
  9805. _sapp.x11.hidden_cursor = XcursorImageLoadCursor(_sapp.x11.display, img);
  9806. XcursorImageDestroy(img);
  9807. }
  9808. _SOKOL_PRIVATE void _sapp_x11_toggle_fullscreen(void) {
  9809. _sapp.fullscreen = !_sapp.fullscreen;
  9810. _sapp_x11_set_fullscreen(_sapp.fullscreen);
  9811. _sapp_x11_query_window_size();
  9812. }
  9813. _SOKOL_PRIVATE void _sapp_x11_show_mouse(bool show) {
  9814. if (show) {
  9815. XUndefineCursor(_sapp.x11.display, _sapp.x11.window);
  9816. }
  9817. else {
  9818. XDefineCursor(_sapp.x11.display, _sapp.x11.window, _sapp.x11.hidden_cursor);
  9819. }
  9820. }
  9821. _SOKOL_PRIVATE void _sapp_x11_lock_mouse(bool lock) {
  9822. if (lock == _sapp.mouse.locked) {
  9823. return;
  9824. }
  9825. _sapp.mouse.dx = 0.0f;
  9826. _sapp.mouse.dy = 0.0f;
  9827. _sapp.mouse.locked = lock;
  9828. if (_sapp.mouse.locked) {
  9829. if (_sapp.x11.xi.available) {
  9830. XIEventMask em;
  9831. unsigned char mask[XIMaskLen(XI_RawMotion)] = { 0 }; // XIMaskLen is a macro
  9832. em.deviceid = XIAllMasterDevices;
  9833. em.mask_len = sizeof(mask);
  9834. em.mask = mask;
  9835. XISetMask(mask, XI_RawMotion);
  9836. XISelectEvents(_sapp.x11.display, _sapp.x11.root, &em, 1);
  9837. }
  9838. XGrabPointer(_sapp.x11.display, // display
  9839. _sapp.x11.window, // grab_window
  9840. True, // owner_events
  9841. ButtonPressMask | ButtonReleaseMask | PointerMotionMask, // event_mask
  9842. GrabModeAsync, // pointer_mode
  9843. GrabModeAsync, // keyboard_mode
  9844. _sapp.x11.window, // confine_to
  9845. _sapp.x11.hidden_cursor, // cursor
  9846. CurrentTime); // time
  9847. }
  9848. else {
  9849. if (_sapp.x11.xi.available) {
  9850. XIEventMask em;
  9851. unsigned char mask[] = { 0 };
  9852. em.deviceid = XIAllMasterDevices;
  9853. em.mask_len = sizeof(mask);
  9854. em.mask = mask;
  9855. XISelectEvents(_sapp.x11.display, _sapp.x11.root, &em, 1);
  9856. }
  9857. XWarpPointer(_sapp.x11.display, None, _sapp.x11.window, 0, 0, 0, 0, (int) _sapp.mouse.x, _sapp.mouse.y);
  9858. XUngrabPointer(_sapp.x11.display, CurrentTime);
  9859. }
  9860. XFlush(_sapp.x11.display);
  9861. }
  9862. _SOKOL_PRIVATE void _sapp_x11_update_window_title(void) {
  9863. Xutf8SetWMProperties(_sapp.x11.display,
  9864. _sapp.x11.window,
  9865. _sapp.window_title, _sapp.window_title,
  9866. NULL, 0, NULL, NULL, NULL);
  9867. XChangeProperty(_sapp.x11.display, _sapp.x11.window,
  9868. _sapp.x11.NET_WM_NAME, _sapp.x11.UTF8_STRING, 8,
  9869. PropModeReplace,
  9870. (unsigned char*)_sapp.window_title,
  9871. strlen(_sapp.window_title));
  9872. XChangeProperty(_sapp.x11.display, _sapp.x11.window,
  9873. _sapp.x11.NET_WM_ICON_NAME, _sapp.x11.UTF8_STRING, 8,
  9874. PropModeReplace,
  9875. (unsigned char*)_sapp.window_title,
  9876. strlen(_sapp.window_title));
  9877. XFlush(_sapp.x11.display);
  9878. }
  9879. _SOKOL_PRIVATE void _sapp_x11_set_icon(const sapp_icon_desc* icon_desc, int num_images) {
  9880. SOKOL_ASSERT((num_images > 0) && (num_images <= SAPP_MAX_ICONIMAGES));
  9881. int long_count = 0;
  9882. for (int i = 0; i < num_images; i++) {
  9883. const sapp_image_desc* img_desc = &icon_desc->images[i];
  9884. long_count += 2 + (img_desc->width * img_desc->height);
  9885. }
  9886. long* icon_data = (long*) SOKOL_CALLOC((size_t)long_count, sizeof(long));
  9887. SOKOL_ASSERT(icon_data);
  9888. long* dst = icon_data;
  9889. for (int img_index = 0; img_index < num_images; img_index++) {
  9890. const sapp_image_desc* img_desc = &icon_desc->images[img_index];
  9891. const uint8_t* src = (const uint8_t*) img_desc->pixels.ptr;
  9892. *dst++ = img_desc->width;
  9893. *dst++ = img_desc->height;
  9894. const int num_pixels = img_desc->width * img_desc->height;
  9895. for (int pixel_index = 0; pixel_index < num_pixels; pixel_index++) {
  9896. *dst++ = (src[pixel_index * 4 + 0] << 16) |
  9897. (src[pixel_index * 4 + 1] << 8) |
  9898. (src[pixel_index * 4 + 2] << 0) |
  9899. (src[pixel_index * 4 + 3] << 24);
  9900. }
  9901. }
  9902. XChangeProperty(_sapp.x11.display, _sapp.x11.window,
  9903. _sapp.x11.NET_WM_ICON,
  9904. XA_CARDINAL, 32,
  9905. PropModeReplace,
  9906. (unsigned char*)icon_data,
  9907. long_count);
  9908. SOKOL_FREE(icon_data);
  9909. XFlush(_sapp.x11.display);
  9910. }
  9911. _SOKOL_PRIVATE void _sapp_x11_create_window(Visual* visual, int depth) {
  9912. _sapp.x11.colormap = XCreateColormap(_sapp.x11.display, _sapp.x11.root, visual, AllocNone);
  9913. XSetWindowAttributes wa;
  9914. memset(&wa, 0, sizeof(wa));
  9915. const uint32_t wamask = CWBorderPixel | CWColormap | CWEventMask;
  9916. wa.colormap = _sapp.x11.colormap;
  9917. wa.border_pixel = 0;
  9918. wa.event_mask = StructureNotifyMask | KeyPressMask | KeyReleaseMask |
  9919. PointerMotionMask | ButtonPressMask | ButtonReleaseMask |
  9920. ExposureMask | FocusChangeMask | VisibilityChangeMask |
  9921. EnterWindowMask | LeaveWindowMask | PropertyChangeMask;
  9922. _sapp_x11_grab_error_handler();
  9923. _sapp.x11.window = XCreateWindow(_sapp.x11.display,
  9924. _sapp.x11.root,
  9925. 0, 0,
  9926. (uint32_t)_sapp.window_width,
  9927. (uint32_t)_sapp.window_height,
  9928. 0, /* border width */
  9929. depth, /* color depth */
  9930. InputOutput,
  9931. visual,
  9932. wamask,
  9933. &wa);
  9934. _sapp_x11_release_error_handler();
  9935. if (!_sapp.x11.window) {
  9936. _sapp_fail("X11: Failed to create window");
  9937. }
  9938. Atom protocols[] = {
  9939. _sapp.x11.WM_DELETE_WINDOW
  9940. };
  9941. XSetWMProtocols(_sapp.x11.display, _sapp.x11.window, protocols, 1);
  9942. XSizeHints* hints = XAllocSizeHints();
  9943. hints->flags |= PWinGravity;
  9944. hints->win_gravity = StaticGravity;
  9945. XSetWMNormalHints(_sapp.x11.display, _sapp.x11.window, hints);
  9946. XFree(hints);
  9947. /* announce support for drag'n'drop */
  9948. if (_sapp.drop.enabled) {
  9949. const Atom version = _SAPP_X11_XDND_VERSION;
  9950. XChangeProperty(_sapp.x11.display, _sapp.x11.window, _sapp.x11.xdnd.XdndAware, XA_ATOM, 32, PropModeReplace, (unsigned char*) &version, 1);
  9951. }
  9952. _sapp_x11_update_window_title();
  9953. }
  9954. _SOKOL_PRIVATE void _sapp_x11_destroy_window(void) {
  9955. if (_sapp.x11.window) {
  9956. XUnmapWindow(_sapp.x11.display, _sapp.x11.window);
  9957. XDestroyWindow(_sapp.x11.display, _sapp.x11.window);
  9958. _sapp.x11.window = 0;
  9959. }
  9960. if (_sapp.x11.colormap) {
  9961. XFreeColormap(_sapp.x11.display, _sapp.x11.colormap);
  9962. _sapp.x11.colormap = 0;
  9963. }
  9964. XFlush(_sapp.x11.display);
  9965. }
  9966. _SOKOL_PRIVATE bool _sapp_x11_window_visible(void) {
  9967. XWindowAttributes wa;
  9968. XGetWindowAttributes(_sapp.x11.display, _sapp.x11.window, &wa);
  9969. return wa.map_state == IsViewable;
  9970. }
  9971. _SOKOL_PRIVATE void _sapp_x11_show_window(void) {
  9972. if (!_sapp_x11_window_visible()) {
  9973. XMapWindow(_sapp.x11.display, _sapp.x11.window);
  9974. XRaiseWindow(_sapp.x11.display, _sapp.x11.window);
  9975. XFlush(_sapp.x11.display);
  9976. }
  9977. }
  9978. _SOKOL_PRIVATE void _sapp_x11_hide_window(void) {
  9979. XUnmapWindow(_sapp.x11.display, _sapp.x11.window);
  9980. XFlush(_sapp.x11.display);
  9981. }
  9982. _SOKOL_PRIVATE unsigned long _sapp_x11_get_window_property(Window window, Atom property, Atom type, unsigned char** value) {
  9983. Atom actualType;
  9984. int actualFormat;
  9985. unsigned long itemCount, bytesAfter;
  9986. XGetWindowProperty(_sapp.x11.display,
  9987. window,
  9988. property,
  9989. 0,
  9990. LONG_MAX,
  9991. False,
  9992. type,
  9993. &actualType,
  9994. &actualFormat,
  9995. &itemCount,
  9996. &bytesAfter,
  9997. value);
  9998. return itemCount;
  9999. }
  10000. _SOKOL_PRIVATE int _sapp_x11_get_window_state(void) {
  10001. int result = WithdrawnState;
  10002. struct {
  10003. CARD32 state;
  10004. Window icon;
  10005. } *state = NULL;
  10006. if (_sapp_x11_get_window_property(_sapp.x11.window, _sapp.x11.WM_STATE, _sapp.x11.WM_STATE, (unsigned char**)&state) >= 2) {
  10007. result = (int)state->state;
  10008. }
  10009. if (state) {
  10010. XFree(state);
  10011. }
  10012. return result;
  10013. }
  10014. _SOKOL_PRIVATE uint32_t _sapp_x11_key_modifier_bit(sapp_keycode key) {
  10015. switch (key) {
  10016. case SAPP_KEYCODE_LEFT_SHIFT:
  10017. case SAPP_KEYCODE_RIGHT_SHIFT:
  10018. return SAPP_MODIFIER_SHIFT;
  10019. case SAPP_KEYCODE_LEFT_CONTROL:
  10020. case SAPP_KEYCODE_RIGHT_CONTROL:
  10021. return SAPP_MODIFIER_CTRL;
  10022. case SAPP_KEYCODE_LEFT_ALT:
  10023. case SAPP_KEYCODE_RIGHT_ALT:
  10024. return SAPP_MODIFIER_ALT;
  10025. case SAPP_KEYCODE_LEFT_SUPER:
  10026. case SAPP_KEYCODE_RIGHT_SUPER:
  10027. return SAPP_MODIFIER_SUPER;
  10028. default:
  10029. return 0;
  10030. }
  10031. }
  10032. _SOKOL_PRIVATE uint32_t _sapp_x11_button_modifier_bit(sapp_mousebutton btn) {
  10033. switch (btn) {
  10034. case SAPP_MOUSEBUTTON_LEFT: return SAPP_MODIFIER_LMB;
  10035. case SAPP_MOUSEBUTTON_RIGHT: return SAPP_MODIFIER_RMB;
  10036. case SAPP_MOUSEBUTTON_MIDDLE: return SAPP_MODIFIER_MMB;
  10037. default: return 0;
  10038. }
  10039. }
  10040. _SOKOL_PRIVATE uint32_t _sapp_x11_mods(uint32_t x11_mods) {
  10041. uint32_t mods = 0;
  10042. if (x11_mods & ShiftMask) {
  10043. mods |= SAPP_MODIFIER_SHIFT;
  10044. }
  10045. if (x11_mods & ControlMask) {
  10046. mods |= SAPP_MODIFIER_CTRL;
  10047. }
  10048. if (x11_mods & Mod1Mask) {
  10049. mods |= SAPP_MODIFIER_ALT;
  10050. }
  10051. if (x11_mods & Mod4Mask) {
  10052. mods |= SAPP_MODIFIER_SUPER;
  10053. }
  10054. if (x11_mods & Button1Mask) {
  10055. mods |= SAPP_MODIFIER_LMB;
  10056. }
  10057. if (x11_mods & Button2Mask) {
  10058. mods |= SAPP_MODIFIER_MMB;
  10059. }
  10060. if (x11_mods & Button3Mask) {
  10061. mods |= SAPP_MODIFIER_RMB;
  10062. }
  10063. return mods;
  10064. }
  10065. _SOKOL_PRIVATE void _sapp_x11_app_event(sapp_event_type type) {
  10066. if (_sapp_events_enabled()) {
  10067. _sapp_init_event(type);
  10068. _sapp_call_event(&_sapp.event);
  10069. }
  10070. }
  10071. _SOKOL_PRIVATE sapp_mousebutton _sapp_x11_translate_button(const XEvent* event) {
  10072. switch (event->xbutton.button) {
  10073. case Button1: return SAPP_MOUSEBUTTON_LEFT;
  10074. case Button2: return SAPP_MOUSEBUTTON_MIDDLE;
  10075. case Button3: return SAPP_MOUSEBUTTON_RIGHT;
  10076. default: return SAPP_MOUSEBUTTON_INVALID;
  10077. }
  10078. }
  10079. _SOKOL_PRIVATE void _sapp_x11_mouse_event(sapp_event_type type, sapp_mousebutton btn, uint32_t mods) {
  10080. if (_sapp_events_enabled()) {
  10081. _sapp_init_event(type);
  10082. _sapp.event.mouse_button = btn;
  10083. _sapp.event.modifiers = mods;
  10084. _sapp_call_event(&_sapp.event);
  10085. }
  10086. }
  10087. _SOKOL_PRIVATE void _sapp_x11_scroll_event(float x, float y, uint32_t mods) {
  10088. if (_sapp_events_enabled()) {
  10089. _sapp_init_event(SAPP_EVENTTYPE_MOUSE_SCROLL);
  10090. _sapp.event.modifiers = mods;
  10091. _sapp.event.scroll_x = x;
  10092. _sapp.event.scroll_y = y;
  10093. _sapp_call_event(&_sapp.event);
  10094. }
  10095. }
  10096. _SOKOL_PRIVATE void _sapp_x11_key_event(sapp_event_type type, sapp_keycode key, bool repeat, uint32_t mods) {
  10097. if (_sapp_events_enabled()) {
  10098. _sapp_init_event(type);
  10099. _sapp.event.key_code = key;
  10100. _sapp.event.key_repeat = repeat;
  10101. _sapp.event.modifiers = mods;
  10102. _sapp_call_event(&_sapp.event);
  10103. /* check if a CLIPBOARD_PASTED event must be sent too */
  10104. if (_sapp.clipboard.enabled &&
  10105. (type == SAPP_EVENTTYPE_KEY_DOWN) &&
  10106. (_sapp.event.modifiers == SAPP_MODIFIER_CTRL) &&
  10107. (_sapp.event.key_code == SAPP_KEYCODE_V))
  10108. {
  10109. _sapp_init_event(SAPP_EVENTTYPE_CLIPBOARD_PASTED);
  10110. _sapp_call_event(&_sapp.event);
  10111. }
  10112. }
  10113. }
  10114. _SOKOL_PRIVATE void _sapp_x11_char_event(uint32_t chr, bool repeat, uint32_t mods) {
  10115. if (_sapp_events_enabled()) {
  10116. _sapp_init_event(SAPP_EVENTTYPE_CHAR);
  10117. _sapp.event.char_code = chr;
  10118. _sapp.event.key_repeat = repeat;
  10119. _sapp.event.modifiers = mods;
  10120. _sapp_call_event(&_sapp.event);
  10121. }
  10122. }
  10123. _SOKOL_PRIVATE sapp_keycode _sapp_x11_translate_key(int scancode) {
  10124. int dummy;
  10125. KeySym* keysyms = XGetKeyboardMapping(_sapp.x11.display, scancode, 1, &dummy);
  10126. SOKOL_ASSERT(keysyms);
  10127. KeySym keysym = keysyms[0];
  10128. XFree(keysyms);
  10129. switch (keysym) {
  10130. case XK_Escape: return SAPP_KEYCODE_ESCAPE;
  10131. case XK_Tab: return SAPP_KEYCODE_TAB;
  10132. case XK_Shift_L: return SAPP_KEYCODE_LEFT_SHIFT;
  10133. case XK_Shift_R: return SAPP_KEYCODE_RIGHT_SHIFT;
  10134. case XK_Control_L: return SAPP_KEYCODE_LEFT_CONTROL;
  10135. case XK_Control_R: return SAPP_KEYCODE_RIGHT_CONTROL;
  10136. case XK_Meta_L:
  10137. case XK_Alt_L: return SAPP_KEYCODE_LEFT_ALT;
  10138. case XK_Mode_switch: /* Mapped to Alt_R on many keyboards */
  10139. case XK_ISO_Level3_Shift: /* AltGr on at least some machines */
  10140. case XK_Meta_R:
  10141. case XK_Alt_R: return SAPP_KEYCODE_RIGHT_ALT;
  10142. case XK_Super_L: return SAPP_KEYCODE_LEFT_SUPER;
  10143. case XK_Super_R: return SAPP_KEYCODE_RIGHT_SUPER;
  10144. case XK_Menu: return SAPP_KEYCODE_MENU;
  10145. case XK_Num_Lock: return SAPP_KEYCODE_NUM_LOCK;
  10146. case XK_Caps_Lock: return SAPP_KEYCODE_CAPS_LOCK;
  10147. case XK_Print: return SAPP_KEYCODE_PRINT_SCREEN;
  10148. case XK_Scroll_Lock: return SAPP_KEYCODE_SCROLL_LOCK;
  10149. case XK_Pause: return SAPP_KEYCODE_PAUSE;
  10150. case XK_Delete: return SAPP_KEYCODE_DELETE;
  10151. case XK_BackSpace: return SAPP_KEYCODE_BACKSPACE;
  10152. case XK_Return: return SAPP_KEYCODE_ENTER;
  10153. case XK_Home: return SAPP_KEYCODE_HOME;
  10154. case XK_End: return SAPP_KEYCODE_END;
  10155. case XK_Page_Up: return SAPP_KEYCODE_PAGE_UP;
  10156. case XK_Page_Down: return SAPP_KEYCODE_PAGE_DOWN;
  10157. case XK_Insert: return SAPP_KEYCODE_INSERT;
  10158. case XK_Left: return SAPP_KEYCODE_LEFT;
  10159. case XK_Right: return SAPP_KEYCODE_RIGHT;
  10160. case XK_Down: return SAPP_KEYCODE_DOWN;
  10161. case XK_Up: return SAPP_KEYCODE_UP;
  10162. case XK_F1: return SAPP_KEYCODE_F1;
  10163. case XK_F2: return SAPP_KEYCODE_F2;
  10164. case XK_F3: return SAPP_KEYCODE_F3;
  10165. case XK_F4: return SAPP_KEYCODE_F4;
  10166. case XK_F5: return SAPP_KEYCODE_F5;
  10167. case XK_F6: return SAPP_KEYCODE_F6;
  10168. case XK_F7: return SAPP_KEYCODE_F7;
  10169. case XK_F8: return SAPP_KEYCODE_F8;
  10170. case XK_F9: return SAPP_KEYCODE_F9;
  10171. case XK_F10: return SAPP_KEYCODE_F10;
  10172. case XK_F11: return SAPP_KEYCODE_F11;
  10173. case XK_F12: return SAPP_KEYCODE_F12;
  10174. case XK_F13: return SAPP_KEYCODE_F13;
  10175. case XK_F14: return SAPP_KEYCODE_F14;
  10176. case XK_F15: return SAPP_KEYCODE_F15;
  10177. case XK_F16: return SAPP_KEYCODE_F16;
  10178. case XK_F17: return SAPP_KEYCODE_F17;
  10179. case XK_F18: return SAPP_KEYCODE_F18;
  10180. case XK_F19: return SAPP_KEYCODE_F19;
  10181. case XK_F20: return SAPP_KEYCODE_F20;
  10182. case XK_F21: return SAPP_KEYCODE_F21;
  10183. case XK_F22: return SAPP_KEYCODE_F22;
  10184. case XK_F23: return SAPP_KEYCODE_F23;
  10185. case XK_F24: return SAPP_KEYCODE_F24;
  10186. case XK_F25: return SAPP_KEYCODE_F25;
  10187. case XK_KP_Divide: return SAPP_KEYCODE_KP_DIVIDE;
  10188. case XK_KP_Multiply: return SAPP_KEYCODE_KP_MULTIPLY;
  10189. case XK_KP_Subtract: return SAPP_KEYCODE_KP_SUBTRACT;
  10190. case XK_KP_Add: return SAPP_KEYCODE_KP_ADD;
  10191. case XK_KP_Insert: return SAPP_KEYCODE_KP_0;
  10192. case XK_KP_End: return SAPP_KEYCODE_KP_1;
  10193. case XK_KP_Down: return SAPP_KEYCODE_KP_2;
  10194. case XK_KP_Page_Down: return SAPP_KEYCODE_KP_3;
  10195. case XK_KP_Left: return SAPP_KEYCODE_KP_4;
  10196. case XK_KP_Begin: return SAPP_KEYCODE_KP_5;
  10197. case XK_KP_Right: return SAPP_KEYCODE_KP_6;
  10198. case XK_KP_Home: return SAPP_KEYCODE_KP_7;
  10199. case XK_KP_Up: return SAPP_KEYCODE_KP_8;
  10200. case XK_KP_Page_Up: return SAPP_KEYCODE_KP_9;
  10201. case XK_KP_Delete: return SAPP_KEYCODE_KP_DECIMAL;
  10202. case XK_KP_Equal: return SAPP_KEYCODE_KP_EQUAL;
  10203. case XK_KP_Enter: return SAPP_KEYCODE_KP_ENTER;
  10204. case XK_a: return SAPP_KEYCODE_A;
  10205. case XK_b: return SAPP_KEYCODE_B;
  10206. case XK_c: return SAPP_KEYCODE_C;
  10207. case XK_d: return SAPP_KEYCODE_D;
  10208. case XK_e: return SAPP_KEYCODE_E;
  10209. case XK_f: return SAPP_KEYCODE_F;
  10210. case XK_g: return SAPP_KEYCODE_G;
  10211. case XK_h: return SAPP_KEYCODE_H;
  10212. case XK_i: return SAPP_KEYCODE_I;
  10213. case XK_j: return SAPP_KEYCODE_J;
  10214. case XK_k: return SAPP_KEYCODE_K;
  10215. case XK_l: return SAPP_KEYCODE_L;
  10216. case XK_m: return SAPP_KEYCODE_M;
  10217. case XK_n: return SAPP_KEYCODE_N;
  10218. case XK_o: return SAPP_KEYCODE_O;
  10219. case XK_p: return SAPP_KEYCODE_P;
  10220. case XK_q: return SAPP_KEYCODE_Q;
  10221. case XK_r: return SAPP_KEYCODE_R;
  10222. case XK_s: return SAPP_KEYCODE_S;
  10223. case XK_t: return SAPP_KEYCODE_T;
  10224. case XK_u: return SAPP_KEYCODE_U;
  10225. case XK_v: return SAPP_KEYCODE_V;
  10226. case XK_w: return SAPP_KEYCODE_W;
  10227. case XK_x: return SAPP_KEYCODE_X;
  10228. case XK_y: return SAPP_KEYCODE_Y;
  10229. case XK_z: return SAPP_KEYCODE_Z;
  10230. case XK_1: return SAPP_KEYCODE_1;
  10231. case XK_2: return SAPP_KEYCODE_2;
  10232. case XK_3: return SAPP_KEYCODE_3;
  10233. case XK_4: return SAPP_KEYCODE_4;
  10234. case XK_5: return SAPP_KEYCODE_5;
  10235. case XK_6: return SAPP_KEYCODE_6;
  10236. case XK_7: return SAPP_KEYCODE_7;
  10237. case XK_8: return SAPP_KEYCODE_8;
  10238. case XK_9: return SAPP_KEYCODE_9;
  10239. case XK_0: return SAPP_KEYCODE_0;
  10240. case XK_space: return SAPP_KEYCODE_SPACE;
  10241. case XK_minus: return SAPP_KEYCODE_MINUS;
  10242. case XK_equal: return SAPP_KEYCODE_EQUAL;
  10243. case XK_bracketleft: return SAPP_KEYCODE_LEFT_BRACKET;
  10244. case XK_bracketright: return SAPP_KEYCODE_RIGHT_BRACKET;
  10245. case XK_backslash: return SAPP_KEYCODE_BACKSLASH;
  10246. case XK_semicolon: return SAPP_KEYCODE_SEMICOLON;
  10247. case XK_apostrophe: return SAPP_KEYCODE_APOSTROPHE;
  10248. case XK_grave: return SAPP_KEYCODE_GRAVE_ACCENT;
  10249. case XK_comma: return SAPP_KEYCODE_COMMA;
  10250. case XK_period: return SAPP_KEYCODE_PERIOD;
  10251. case XK_slash: return SAPP_KEYCODE_SLASH;
  10252. case XK_less: return SAPP_KEYCODE_WORLD_1; /* At least in some layouts... */
  10253. default: return SAPP_KEYCODE_INVALID;
  10254. }
  10255. }
  10256. _SOKOL_PRIVATE int32_t _sapp_x11_keysym_to_unicode(KeySym keysym) {
  10257. int min = 0;
  10258. int max = sizeof(_sapp_x11_keysymtab) / sizeof(struct _sapp_x11_codepair) - 1;
  10259. int mid;
  10260. /* First check for Latin-1 characters (1:1 mapping) */
  10261. if ((keysym >= 0x0020 && keysym <= 0x007e) ||
  10262. (keysym >= 0x00a0 && keysym <= 0x00ff))
  10263. {
  10264. return keysym;
  10265. }
  10266. /* Also check for directly encoded 24-bit UCS characters */
  10267. if ((keysym & 0xff000000) == 0x01000000) {
  10268. return keysym & 0x00ffffff;
  10269. }
  10270. /* Binary search in table */
  10271. while (max >= min) {
  10272. mid = (min + max) / 2;
  10273. if (_sapp_x11_keysymtab[mid].keysym < keysym) {
  10274. min = mid + 1;
  10275. }
  10276. else if (_sapp_x11_keysymtab[mid].keysym > keysym) {
  10277. max = mid - 1;
  10278. }
  10279. else {
  10280. return _sapp_x11_keysymtab[mid].ucs;
  10281. }
  10282. }
  10283. /* No matching Unicode value found */
  10284. return -1;
  10285. }
  10286. _SOKOL_PRIVATE bool _sapp_x11_parse_dropped_files_list(const char* src) {
  10287. SOKOL_ASSERT(src);
  10288. SOKOL_ASSERT(_sapp.drop.buffer);
  10289. _sapp_clear_drop_buffer();
  10290. _sapp.drop.num_files = 0;
  10291. /*
  10292. src is (potentially percent-encoded) string made of one or multiple paths
  10293. separated by \r\n, each path starting with 'file://'
  10294. */
  10295. bool err = false;
  10296. int src_count = 0;
  10297. char src_chr = 0;
  10298. char* dst_ptr = _sapp.drop.buffer;
  10299. const char* dst_end_ptr = dst_ptr + (_sapp.drop.max_path_length - 1); // room for terminating 0
  10300. while (0 != (src_chr = *src++)) {
  10301. src_count++;
  10302. char dst_chr = 0;
  10303. /* check leading 'file://' */
  10304. if (src_count <= 7) {
  10305. if (((src_count == 1) && (src_chr != 'f')) ||
  10306. ((src_count == 2) && (src_chr != 'i')) ||
  10307. ((src_count == 3) && (src_chr != 'l')) ||
  10308. ((src_count == 4) && (src_chr != 'e')) ||
  10309. ((src_count == 5) && (src_chr != ':')) ||
  10310. ((src_count == 6) && (src_chr != '/')) ||
  10311. ((src_count == 7) && (src_chr != '/')))
  10312. {
  10313. SOKOL_LOG("sokol_app.h: dropped file URI doesn't start with file://");
  10314. err = true;
  10315. break;
  10316. }
  10317. }
  10318. else if (src_chr == '\r') {
  10319. // skip
  10320. }
  10321. else if (src_chr == '\n') {
  10322. src_chr = 0;
  10323. src_count = 0;
  10324. _sapp.drop.num_files++;
  10325. // too many files is not an error
  10326. if (_sapp.drop.num_files >= _sapp.drop.max_files) {
  10327. break;
  10328. }
  10329. dst_ptr = _sapp.drop.buffer + _sapp.drop.num_files * _sapp.drop.max_path_length;
  10330. dst_end_ptr = dst_ptr + (_sapp.drop.max_path_length - 1);
  10331. }
  10332. else if ((src_chr == '%') && src[0] && src[1]) {
  10333. // a percent-encoded byte (most like UTF-8 multibyte sequence)
  10334. const char digits[3] = { src[0], src[1], 0 };
  10335. src += 2;
  10336. dst_chr = (char) strtol(digits, 0, 16);
  10337. }
  10338. else {
  10339. dst_chr = src_chr;
  10340. }
  10341. if (dst_chr) {
  10342. // dst_end_ptr already has adjustment for terminating zero
  10343. if (dst_ptr < dst_end_ptr) {
  10344. *dst_ptr++ = dst_chr;
  10345. }
  10346. else {
  10347. SOKOL_LOG("sokol_app.h: dropped file path too long (sapp_desc.max_dropped_file_path_length)");
  10348. err = true;
  10349. break;
  10350. }
  10351. }
  10352. }
  10353. if (err) {
  10354. _sapp_clear_drop_buffer();
  10355. _sapp.drop.num_files = 0;
  10356. return false;
  10357. }
  10358. else {
  10359. return true;
  10360. }
  10361. }
  10362. // XLib manual says keycodes are in the range [8, 255] inclusive.
  10363. // https://tronche.com/gui/x/xlib/input/keyboard-encoding.html
  10364. static bool _sapp_x11_keycodes[256];
  10365. _SOKOL_PRIVATE void _sapp_x11_process_event(XEvent* event) {
  10366. Bool filtered = XFilterEvent(event, None);
  10367. switch (event->type) {
  10368. case GenericEvent:
  10369. if (_sapp.mouse.locked && _sapp.x11.xi.available) {
  10370. if (event->xcookie.extension == _sapp.x11.xi.major_opcode) {
  10371. if (XGetEventData(_sapp.x11.display, &event->xcookie)) {
  10372. if (event->xcookie.evtype == XI_RawMotion) {
  10373. XIRawEvent* re = (XIRawEvent*) event->xcookie.data;
  10374. if (re->valuators.mask_len) {
  10375. const double* values = re->raw_values;
  10376. if (XIMaskIsSet(re->valuators.mask, 0)) {
  10377. _sapp.mouse.dx = (float) *values;
  10378. values++;
  10379. }
  10380. if (XIMaskIsSet(re->valuators.mask, 1)) {
  10381. _sapp.mouse.dy = (float) *values;
  10382. }
  10383. _sapp_x11_mouse_event(SAPP_EVENTTYPE_MOUSE_MOVE, SAPP_MOUSEBUTTON_INVALID, _sapp_x11_mods(event->xmotion.state));
  10384. }
  10385. }
  10386. XFreeEventData(_sapp.x11.display, &event->xcookie);
  10387. }
  10388. }
  10389. }
  10390. break;
  10391. case FocusOut:
  10392. /* if focus is lost for any reason, and we're in mouse locked mode, disable mouse lock */
  10393. if (_sapp.mouse.locked) {
  10394. _sapp_x11_lock_mouse(false);
  10395. }
  10396. break;
  10397. case KeyPress:
  10398. {
  10399. int keycode = (int)event->xkey.keycode;
  10400. const sapp_keycode key = _sapp_x11_translate_key(keycode);
  10401. bool repeat = _sapp_x11_keycodes[keycode & 0xFF];
  10402. _sapp_x11_keycodes[keycode & 0xFF] = true;
  10403. uint32_t mods = _sapp_x11_mods(event->xkey.state);
  10404. // X11 doesn't set modifier bit on key down, so emulate that
  10405. mods |= _sapp_x11_key_modifier_bit(key);
  10406. if (key != SAPP_KEYCODE_INVALID) {
  10407. _sapp_x11_key_event(SAPP_EVENTTYPE_KEY_DOWN, key, repeat, mods);
  10408. }
  10409. KeySym keysym;
  10410. XLookupString(&event->xkey, NULL, 0, &keysym, NULL);
  10411. int32_t chr = _sapp_x11_keysym_to_unicode(keysym);
  10412. if (chr > 0) {
  10413. _sapp_x11_char_event((uint32_t)chr, repeat, mods);
  10414. }
  10415. }
  10416. break;
  10417. case KeyRelease:
  10418. {
  10419. int keycode = (int)event->xkey.keycode;
  10420. const sapp_keycode key = _sapp_x11_translate_key(keycode);
  10421. _sapp_x11_keycodes[keycode & 0xFF] = false;
  10422. if (key != SAPP_KEYCODE_INVALID) {
  10423. uint32_t mods = _sapp_x11_mods(event->xkey.state);
  10424. // X11 doesn't clear modifier bit on key up, so emulate that
  10425. mods &= ~_sapp_x11_key_modifier_bit(key);
  10426. _sapp_x11_key_event(SAPP_EVENTTYPE_KEY_UP, key, false, mods);
  10427. }
  10428. }
  10429. break;
  10430. case ButtonPress:
  10431. {
  10432. const sapp_mousebutton btn = _sapp_x11_translate_button(event);
  10433. uint32_t mods = _sapp_x11_mods(event->xbutton.state);
  10434. // X11 doesn't set modifier bit on button down, so emulate that
  10435. mods |= _sapp_x11_button_modifier_bit(btn);
  10436. if (btn != SAPP_MOUSEBUTTON_INVALID) {
  10437. _sapp_x11_mouse_event(SAPP_EVENTTYPE_MOUSE_DOWN, btn, mods);
  10438. _sapp.x11.mouse_buttons |= (1 << btn);
  10439. }
  10440. else {
  10441. /* might be a scroll event */
  10442. switch (event->xbutton.button) {
  10443. case 4: _sapp_x11_scroll_event(0.0f, 1.0f, mods); break;
  10444. case 5: _sapp_x11_scroll_event(0.0f, -1.0f, mods); break;
  10445. case 6: _sapp_x11_scroll_event(1.0f, 0.0f, mods); break;
  10446. case 7: _sapp_x11_scroll_event(-1.0f, 0.0f, mods); break;
  10447. }
  10448. }
  10449. }
  10450. break;
  10451. case ButtonRelease:
  10452. {
  10453. const sapp_mousebutton btn = _sapp_x11_translate_button(event);
  10454. if (btn != SAPP_MOUSEBUTTON_INVALID) {
  10455. uint32_t mods = _sapp_x11_mods(event->xbutton.state);
  10456. // X11 doesn't clear modifier bit on button up, so emulate that
  10457. mods &= ~_sapp_x11_button_modifier_bit(btn);
  10458. _sapp_x11_mouse_event(SAPP_EVENTTYPE_MOUSE_UP, btn, mods);
  10459. _sapp.x11.mouse_buttons &= ~(1 << btn);
  10460. }
  10461. }
  10462. break;
  10463. case EnterNotify:
  10464. /* don't send enter/leave events while mouse button held down */
  10465. if (0 == _sapp.x11.mouse_buttons) {
  10466. _sapp_x11_mouse_event(SAPP_EVENTTYPE_MOUSE_ENTER, SAPP_MOUSEBUTTON_INVALID, _sapp_x11_mods(event->xcrossing.state));
  10467. }
  10468. break;
  10469. case LeaveNotify:
  10470. if (0 == _sapp.x11.mouse_buttons) {
  10471. _sapp_x11_mouse_event(SAPP_EVENTTYPE_MOUSE_LEAVE, SAPP_MOUSEBUTTON_INVALID, _sapp_x11_mods(event->xcrossing.state));
  10472. }
  10473. break;
  10474. case MotionNotify:
  10475. if (!_sapp.mouse.locked) {
  10476. const float new_x = (float) event->xmotion.x;
  10477. const float new_y = (float) event->xmotion.y;
  10478. if (_sapp.mouse.pos_valid) {
  10479. _sapp.mouse.dx = new_x - _sapp.mouse.x;
  10480. _sapp.mouse.dy = new_y - _sapp.mouse.y;
  10481. }
  10482. _sapp.mouse.x = new_x;
  10483. _sapp.mouse.y = new_y;
  10484. _sapp.mouse.pos_valid = true;
  10485. _sapp_x11_mouse_event(SAPP_EVENTTYPE_MOUSE_MOVE, SAPP_MOUSEBUTTON_INVALID, _sapp_x11_mods(event->xmotion.state));
  10486. }
  10487. break;
  10488. case ConfigureNotify:
  10489. if ((event->xconfigure.width != _sapp.window_width) || (event->xconfigure.height != _sapp.window_height)) {
  10490. _sapp.window_width = event->xconfigure.width;
  10491. _sapp.window_height = event->xconfigure.height;
  10492. _sapp.framebuffer_width = _sapp.window_width;
  10493. _sapp.framebuffer_height = _sapp.window_height;
  10494. _sapp_x11_app_event(SAPP_EVENTTYPE_RESIZED);
  10495. }
  10496. break;
  10497. case PropertyNotify:
  10498. if (event->xproperty.state == PropertyNewValue) {
  10499. if (event->xproperty.atom == _sapp.x11.WM_STATE) {
  10500. const int state = _sapp_x11_get_window_state();
  10501. if (state != _sapp.x11.window_state) {
  10502. _sapp.x11.window_state = state;
  10503. if (state == IconicState) {
  10504. _sapp_x11_app_event(SAPP_EVENTTYPE_ICONIFIED);
  10505. }
  10506. else if (state == NormalState) {
  10507. _sapp_x11_app_event(SAPP_EVENTTYPE_RESTORED);
  10508. }
  10509. }
  10510. }
  10511. }
  10512. break;
  10513. case ClientMessage:
  10514. if (filtered) {
  10515. return;
  10516. }
  10517. if (event->xclient.message_type == _sapp.x11.WM_PROTOCOLS) {
  10518. const Atom protocol = (Atom)event->xclient.data.l[0];
  10519. if (protocol == _sapp.x11.WM_DELETE_WINDOW) {
  10520. _sapp.quit_requested = true;
  10521. }
  10522. }
  10523. else if (event->xclient.message_type == _sapp.x11.xdnd.XdndEnter) {
  10524. const bool is_list = 0 != (event->xclient.data.l[1] & 1);
  10525. _sapp.x11.xdnd.source = (Window)event->xclient.data.l[0];
  10526. _sapp.x11.xdnd.version = event->xclient.data.l[1] >> 24;
  10527. _sapp.x11.xdnd.format = None;
  10528. if (_sapp.x11.xdnd.version > _SAPP_X11_XDND_VERSION) {
  10529. return;
  10530. }
  10531. uint32_t count = 0;
  10532. Atom* formats = 0;
  10533. if (is_list) {
  10534. count = _sapp_x11_get_window_property(_sapp.x11.xdnd.source, _sapp.x11.xdnd.XdndTypeList, XA_ATOM, (unsigned char**)&formats);
  10535. }
  10536. else {
  10537. count = 3;
  10538. formats = (Atom*) event->xclient.data.l + 2;
  10539. }
  10540. for (uint32_t i = 0; i < count; i++) {
  10541. if (formats[i] == _sapp.x11.xdnd.text_uri_list) {
  10542. _sapp.x11.xdnd.format = _sapp.x11.xdnd.text_uri_list;
  10543. break;
  10544. }
  10545. }
  10546. if (is_list && formats) {
  10547. XFree(formats);
  10548. }
  10549. }
  10550. else if (event->xclient.message_type == _sapp.x11.xdnd.XdndDrop) {
  10551. if (_sapp.x11.xdnd.version > _SAPP_X11_XDND_VERSION) {
  10552. return;
  10553. }
  10554. Time time = CurrentTime;
  10555. if (_sapp.x11.xdnd.format) {
  10556. if (_sapp.x11.xdnd.version >= 1) {
  10557. time = (Time)event->xclient.data.l[2];
  10558. }
  10559. XConvertSelection(_sapp.x11.display,
  10560. _sapp.x11.xdnd.XdndSelection,
  10561. _sapp.x11.xdnd.format,
  10562. _sapp.x11.xdnd.XdndSelection,
  10563. _sapp.x11.window,
  10564. time);
  10565. }
  10566. else if (_sapp.x11.xdnd.version >= 2) {
  10567. XEvent reply;
  10568. memset(&reply, 0, sizeof(reply));
  10569. reply.type = ClientMessage;
  10570. reply.xclient.window = _sapp.x11.window;
  10571. reply.xclient.message_type = _sapp.x11.xdnd.XdndFinished;
  10572. reply.xclient.format = 32;
  10573. reply.xclient.data.l[0] = (long)_sapp.x11.window;
  10574. reply.xclient.data.l[1] = 0; // drag was rejected
  10575. reply.xclient.data.l[2] = None;
  10576. XSendEvent(_sapp.x11.display, _sapp.x11.xdnd.source, False, NoEventMask, &reply);
  10577. XFlush(_sapp.x11.display);
  10578. }
  10579. }
  10580. else if (event->xclient.message_type == _sapp.x11.xdnd.XdndPosition) {
  10581. /* drag operation has moved over the window
  10582. FIXME: we could track the mouse position here, but
  10583. this isn't implemented on other platforms either so far
  10584. */
  10585. if (_sapp.x11.xdnd.version > _SAPP_X11_XDND_VERSION) {
  10586. return;
  10587. }
  10588. XEvent reply;
  10589. memset(&reply, 0, sizeof(reply));
  10590. reply.type = ClientMessage;
  10591. reply.xclient.window = _sapp.x11.xdnd.source;
  10592. reply.xclient.message_type = _sapp.x11.xdnd.XdndStatus;
  10593. reply.xclient.format = 32;
  10594. reply.xclient.data.l[0] = (long)_sapp.x11.window;
  10595. if (_sapp.x11.xdnd.format) {
  10596. /* reply that we are ready to copy the dragged data */
  10597. reply.xclient.data.l[1] = 1; // accept with no rectangle
  10598. if (_sapp.x11.xdnd.version >= 2) {
  10599. reply.xclient.data.l[4] = (long)_sapp.x11.xdnd.XdndActionCopy;
  10600. }
  10601. }
  10602. XSendEvent(_sapp.x11.display, _sapp.x11.xdnd.source, False, NoEventMask, &reply);
  10603. XFlush(_sapp.x11.display);
  10604. }
  10605. break;
  10606. case SelectionNotify:
  10607. if (event->xselection.property == _sapp.x11.xdnd.XdndSelection) {
  10608. char* data = 0;
  10609. uint32_t result = _sapp_x11_get_window_property(event->xselection.requestor,
  10610. event->xselection.property,
  10611. event->xselection.target,
  10612. (unsigned char**) &data);
  10613. if (_sapp.drop.enabled && result) {
  10614. if (_sapp_x11_parse_dropped_files_list(data)) {
  10615. if (_sapp_events_enabled()) {
  10616. _sapp_init_event(SAPP_EVENTTYPE_FILES_DROPPED);
  10617. _sapp_call_event(&_sapp.event);
  10618. }
  10619. }
  10620. }
  10621. if (_sapp.x11.xdnd.version >= 2) {
  10622. XEvent reply;
  10623. memset(&reply, 0, sizeof(reply));
  10624. reply.type = ClientMessage;
  10625. reply.xclient.window = _sapp.x11.window;
  10626. reply.xclient.message_type = _sapp.x11.xdnd.XdndFinished;
  10627. reply.xclient.format = 32;
  10628. reply.xclient.data.l[0] = (long)_sapp.x11.window;
  10629. reply.xclient.data.l[1] = result;
  10630. reply.xclient.data.l[2] = (long)_sapp.x11.xdnd.XdndActionCopy;
  10631. XSendEvent(_sapp.x11.display, _sapp.x11.xdnd.source, False, NoEventMask, &reply);
  10632. XFlush(_sapp.x11.display);
  10633. }
  10634. }
  10635. break;
  10636. case DestroyNotify:
  10637. break;
  10638. }
  10639. }
  10640. _SOKOL_PRIVATE void _sapp_linux_run(const sapp_desc* desc) {
  10641. /* The following lines are here to trigger a linker error instead of an
  10642. obscure runtime error if the user has forgotten to add -pthread to
  10643. the compiler or linker options. They have no other purpose.
  10644. */
  10645. pthread_attr_t pthread_attr;
  10646. pthread_attr_init(&pthread_attr);
  10647. pthread_attr_destroy(&pthread_attr);
  10648. _sapp_init_state(desc);
  10649. _sapp.x11.window_state = NormalState;
  10650. XInitThreads();
  10651. XrmInitialize();
  10652. _sapp.x11.display = XOpenDisplay(NULL);
  10653. if (!_sapp.x11.display) {
  10654. _sapp_fail("XOpenDisplay() failed!\n");
  10655. }
  10656. _sapp.x11.screen = DefaultScreen(_sapp.x11.display);
  10657. _sapp.x11.root = DefaultRootWindow(_sapp.x11.display);
  10658. XkbSetDetectableAutoRepeat(_sapp.x11.display, true, NULL);
  10659. _sapp_x11_query_system_dpi();
  10660. _sapp.dpi_scale = _sapp.x11.dpi / 96.0f;
  10661. _sapp_x11_init_extensions();
  10662. _sapp_x11_create_hidden_cursor();
  10663. _sapp_glx_init();
  10664. Visual* visual = 0;
  10665. int depth = 0;
  10666. _sapp_glx_choose_visual(&visual, &depth);
  10667. _sapp_x11_create_window(visual, depth);
  10668. _sapp_glx_create_context();
  10669. _sapp_set_icon(&desc->icon);
  10670. _sapp.valid = true;
  10671. _sapp_x11_show_window();
  10672. if (_sapp.fullscreen) {
  10673. _sapp_x11_set_fullscreen(true);
  10674. }
  10675. _sapp_x11_query_window_size();
  10676. _sapp_glx_swapinterval(_sapp.swap_interval);
  10677. XFlush(_sapp.x11.display);
  10678. while (!_sapp.quit_ordered) {
  10679. _sapp_glx_make_current();
  10680. int count = XPending(_sapp.x11.display);
  10681. while (count--) {
  10682. XEvent event;
  10683. XNextEvent(_sapp.x11.display, &event);
  10684. _sapp_x11_process_event(&event);
  10685. }
  10686. _sapp_frame();
  10687. _sapp_glx_swap_buffers();
  10688. XFlush(_sapp.x11.display);
  10689. /* handle quit-requested, either from window or from sapp_request_quit() */
  10690. if (_sapp.quit_requested && !_sapp.quit_ordered) {
  10691. /* give user code a chance to intervene */
  10692. _sapp_x11_app_event(SAPP_EVENTTYPE_QUIT_REQUESTED);
  10693. /* if user code hasn't intervened, quit the app */
  10694. if (_sapp.quit_requested) {
  10695. _sapp.quit_ordered = true;
  10696. }
  10697. }
  10698. }
  10699. _sapp_call_cleanup();
  10700. _sapp_glx_destroy_context();
  10701. _sapp_x11_destroy_window();
  10702. XCloseDisplay(_sapp.x11.display);
  10703. _sapp_discard_state();
  10704. }
  10705. #if !defined(SOKOL_NO_ENTRY)
  10706. int main(int argc, char* argv[]) {
  10707. sapp_desc desc = sokol_main(argc, argv);
  10708. _sapp_linux_run(&desc);
  10709. return 0;
  10710. }
  10711. #endif /* SOKOL_NO_ENTRY */
  10712. #endif /* _SAPP_LINUX */
  10713. /*== single- vs multi-window helper functions ================================*/
  10714. _SOKOL_PRIVATE int _sapp_window_sample_count(uint32_t win_id) {
  10715. const _sapp_window_t* win = _sapp_lookup_window(win_id);
  10716. return win ? win->desc.sample_count : 1;
  10717. }
  10718. _SOKOL_PRIVATE int _sapp_window_width(uint32_t win_id) {
  10719. const _sapp_window_t* win = _sapp_lookup_window(win_id);
  10720. if (win && (win->framebuffer_width > 0)) {
  10721. return win->framebuffer_width;
  10722. }
  10723. else {
  10724. return 1;
  10725. }
  10726. }
  10727. _SOKOL_PRIVATE int _sapp_window_height(uint32_t win_id) {
  10728. const _sapp_window_t* win = _sapp_lookup_window(win_id);
  10729. if (win && (win->framebuffer_height > 0)) {
  10730. return win->framebuffer_height;
  10731. }
  10732. else {
  10733. return 1;
  10734. }
  10735. }
  10736. _SOKOL_PRIVATE void _sapp_window_set_client_size(uint32_t win_id, int w, int h) {
  10737. _sapp_window_t* win = _sapp_lookup_window(win_id);
  10738. if (win) {
  10739. _sapp_platform_set_window_size(win, w, h);
  10740. }
  10741. }
  10742. _SOKOL_PRIVATE int _sapp_window_client_width(uint32_t win_id) {
  10743. _sapp_window_t* win = _sapp_lookup_window(win_id);
  10744. if (win) {
  10745. int w;
  10746. _sapp_platform_get_window_size(win, &w, 0);
  10747. return w;
  10748. }
  10749. else {
  10750. return 1;
  10751. }
  10752. }
  10753. _SOKOL_PRIVATE int _sapp_window_client_height(uint32_t win_id) {
  10754. _sapp_window_t* win = _sapp_lookup_window(win_id);
  10755. if (win) {
  10756. int h;
  10757. _sapp_platform_get_window_size(win, 0, &h);
  10758. return h;
  10759. }
  10760. else {
  10761. return 1;
  10762. }
  10763. }
  10764. _SOKOL_PRIVATE void _sapp_window_set_client_pos(uint32_t win_id, int x, int y) {
  10765. _sapp_window_t* win = _sapp_lookup_window(win_id);
  10766. if (win) {
  10767. _sapp_platform_set_window_pos(win, x, y);
  10768. }
  10769. }
  10770. _SOKOL_PRIVATE int _sapp_window_client_posx(uint32_t win_id) {
  10771. _sapp_window_t* win = _sapp_lookup_window(win_id);
  10772. if (win) {
  10773. int x;
  10774. _sapp_platform_get_window_pos(win, &x, 0);
  10775. return x;
  10776. }
  10777. else {
  10778. return 0;
  10779. }
  10780. }
  10781. _SOKOL_PRIVATE int _sapp_window_client_posy(uint32_t win_id) {
  10782. _sapp_window_t* win = _sapp_lookup_window(win_id);
  10783. if (win) {
  10784. int y;
  10785. _sapp_platform_get_window_pos(win, 0, &y);
  10786. return y;
  10787. }
  10788. else {
  10789. return 0;
  10790. }
  10791. }
  10792. _SOKOL_PRIVATE bool _sapp_window_high_dpi(uint32_t win_id) {
  10793. const _sapp_window_t* win = _sapp_lookup_window(win_id);
  10794. if (win) {
  10795. return win->desc.high_dpi && (win->dpi_scale >= 1.5f);
  10796. }
  10797. else {
  10798. return false;
  10799. }
  10800. }
  10801. _SOKOL_PRIVATE float _sapp_window_dpi_scale(uint32_t win_id) {
  10802. const _sapp_window_t* win = _sapp_lookup_window(win_id);
  10803. if (win) {
  10804. return win->dpi_scale;
  10805. }
  10806. else {
  10807. return 1.0f;
  10808. }
  10809. }
  10810. _SOKOL_PRIVATE bool _sapp_window_is_fullscreen(uint32_t win_id) {
  10811. const _sapp_window_t* win = _sapp_lookup_window(win_id);
  10812. if (win) {
  10813. return win->fullscreen;
  10814. }
  10815. else {
  10816. return false;
  10817. }
  10818. }
  10819. _SOKOL_PRIVATE void _sapp_window_toggle_fullscreen(uint32_t win_id) {
  10820. _sapp_window_t* win = _sapp_lookup_window(win_id);
  10821. if (win) {
  10822. #if defined(_SAPP_MACOS)
  10823. _sapp_macos_toggle_fullscreen(win);
  10824. #elif defined(_SAPP_WIN32)
  10825. _sapp_win32_toggle_fullscreen();
  10826. #elif defined(_SAPP_UWP)
  10827. _sapp_uwp_toggle_fullscreen();
  10828. #elif defined(_SAPP_LINUX)
  10829. _sapp_x11_toggle_fullscreen();
  10830. #endif
  10831. }
  10832. }
  10833. _SOKOL_PRIVATE void _sapp_window_set_title(uint32_t win_id, const char* title) {
  10834. SOKOL_ASSERT(title);
  10835. _sapp_window_t* win = _sapp_lookup_window(win_id);
  10836. if (win) {
  10837. _sapp_strcpy(title, win->title, sizeof(win->title));
  10838. #if defined(_SAPP_MACOS)
  10839. _sapp_macos_update_window_title(win);
  10840. #elif defined(_SAPP_WIN32)
  10841. _sapp_win32_update_window_title();
  10842. #elif defined(_SAPP_LINUX)
  10843. _sapp_x11_update_window_title();
  10844. #endif
  10845. }
  10846. }
  10847. _SOKOL_PRIVATE void* _sapp_window_userdata(uint32_t win_id) {
  10848. _sapp_window_t* win = _sapp_lookup_window(win_id);
  10849. if (win) {
  10850. return win->desc.user_data;
  10851. }
  10852. else {
  10853. return 0;
  10854. }
  10855. }
  10856. /*== PUBLIC API FUNCTIONS ====================================================*/
  10857. #if defined(SOKOL_NO_ENTRY)
  10858. SOKOL_API_IMPL void sapp_run(const sapp_desc* desc) {
  10859. SOKOL_ASSERT(desc);
  10860. #if defined(_SAPP_MACOS)
  10861. _sapp_macos_run(desc);
  10862. #elif defined(_SAPP_IOS)
  10863. _sapp_ios_run(desc);
  10864. #elif defined(_SAPP_EMSCRIPTEN)
  10865. _sapp_emsc_run(desc);
  10866. #elif defined(_SAPP_WIN32)
  10867. _sapp_win32_run(desc);
  10868. #elif defined(_SAPP_UWP)
  10869. _sapp_uwp_run(desc);
  10870. #elif defined(_SAPP_LINUX)
  10871. _sapp_linux_run(desc);
  10872. #else
  10873. // calling sapp_run() directly is not supported on Android)
  10874. _sapp_fail("sapp_run() not supported on this platform!");
  10875. #endif
  10876. }
  10877. /* this is just a stub so the linker doesn't complain */
  10878. sapp_desc sokol_main(int argc, char* argv[]) {
  10879. _SOKOL_UNUSED(argc);
  10880. _SOKOL_UNUSED(argv);
  10881. sapp_desc desc;
  10882. memset(&desc, 0, sizeof(desc));
  10883. return desc;
  10884. }
  10885. #else
  10886. /* likewise, in normal mode, sapp_run() is just an empty stub */
  10887. SOKOL_API_IMPL void sapp_run(const sapp_desc* desc) {
  10888. _SOKOL_UNUSED(desc);
  10889. }
  10890. #endif
  10891. SOKOL_API_IMPL bool sapp_isvalid(void) {
  10892. return _sapp.valid;
  10893. }
  10894. SOKOL_API_IMPL sapp_desc sapp_query_desc(void) {
  10895. SOKOL_ASSERT(_sapp.valid);
  10896. return _sapp.desc;
  10897. }
  10898. SOKOL_API_IMPL uint64_t sapp_frame_count(void) {
  10899. SOKOL_ASSERT(_sapp.valid);
  10900. return _sapp.frame_count;
  10901. }
  10902. SOKOL_API_IMPL int sapp_color_format(void) {
  10903. SOKOL_ASSERT(_sapp.valid);
  10904. #if defined(_SAPP_EMSCRIPTEN) && defined(SOKOL_WGPU)
  10905. switch (_sapp.emsc.wgpu.render_format) {
  10906. case WGPUTextureFormat_RGBA8Unorm:
  10907. return _SAPP_PIXELFORMAT_RGBA8;
  10908. case WGPUTextureFormat_BGRA8Unorm:
  10909. return _SAPP_PIXELFORMAT_BGRA8;
  10910. default:
  10911. SOKOL_UNREACHABLE;
  10912. return 0;
  10913. }
  10914. #elif defined(SOKOL_METAL) || defined(SOKOL_D3D11)
  10915. return _SAPP_PIXELFORMAT_BGRA8;
  10916. #else
  10917. return _SAPP_PIXELFORMAT_RGBA8;
  10918. #endif
  10919. }
  10920. SOKOL_API_IMPL int sapp_depth_format(void) {
  10921. SOKOL_ASSERT(_sapp.valid);
  10922. return _SAPP_PIXELFORMAT_DEPTH_STENCIL;
  10923. }
  10924. SOKOL_API_IMPL void sapp_request_quit(void) {
  10925. SOKOL_ASSERT(_sapp.valid);
  10926. _sapp.quit_requested = true;
  10927. }
  10928. SOKOL_API_IMPL void sapp_cancel_quit(void) {
  10929. SOKOL_ASSERT(_sapp.valid);
  10930. _sapp.quit_requested = false;
  10931. }
  10932. SOKOL_API_IMPL void sapp_quit(void) {
  10933. SOKOL_ASSERT(_sapp.valid);
  10934. _sapp.quit_ordered = true;
  10935. }
  10936. SOKOL_API_IMPL void sapp_consume_event(void) {
  10937. SOKOL_ASSERT(_sapp.valid);
  10938. _sapp.event_consumed = true;
  10939. }
  10940. SOKOL_API_IMPL void sapp_show_keyboard(bool show) {
  10941. SOKOL_ASSERT(_sapp.valid);
  10942. #if defined(_SAPP_IOS)
  10943. _sapp_ios_show_keyboard(show);
  10944. #elif defined(_SAPP_EMSCRIPTEN)
  10945. _sapp_emsc_show_keyboard(show);
  10946. #elif defined(_SAPP_ANDROID)
  10947. _sapp_android_show_keyboard(show);
  10948. #else
  10949. _SOKOL_UNUSED(show);
  10950. #endif
  10951. }
  10952. SOKOL_API_IMPL bool sapp_keyboard_shown(void) {
  10953. SOKOL_ASSERT(_sapp.valid);
  10954. return _sapp.onscreen_keyboard_shown;
  10955. }
  10956. /* NOTE that sapp_show_mouse() does not "stack" like the Win32 or macOS API functions! */
  10957. SOKOL_API_IMPL void sapp_show_mouse(bool show) {
  10958. SOKOL_ASSERT(_sapp.valid);
  10959. // FIXME: is this actually a per-window function??
  10960. _sapp_window_t* win = _sapp_lookup_window(_sapp.main_window_id);
  10961. SOKOL_ASSERT(win);
  10962. if (win->mouse.shown != show) {
  10963. #if defined(_SAPP_MACOS)
  10964. _sapp_macos_show_mouse(show);
  10965. #elif defined(_SAPP_WIN32)
  10966. _sapp_win32_show_mouse(show);
  10967. #elif defined(_SAPP_LINUX)
  10968. _sapp_x11_show_mouse(show);
  10969. #elif defined(_SAPP_UWP)
  10970. _sapp_uwp_show_mouse(show);
  10971. #endif
  10972. win->mouse.shown = show;
  10973. }
  10974. }
  10975. SOKOL_API_IMPL bool sapp_mouse_shown(void) {
  10976. SOKOL_ASSERT(_sapp.valid);
  10977. // FIXME: is this actually a per-window function??
  10978. const _sapp_window_t* win = _sapp_lookup_window(_sapp.main_window_id);
  10979. SOKOL_ASSERT(win);
  10980. return win->mouse.shown;
  10981. }
  10982. SOKOL_API_IMPL void sapp_lock_mouse(bool lock) {
  10983. SOKOL_ASSERT(_sapp.valid);
  10984. // FIXME: is this actually a per-window function??
  10985. _sapp_window_t* win = _sapp_lookup_window(_sapp.main_window_id);
  10986. SOKOL_ASSERT(win);
  10987. #if defined(_SAPP_MACOS)
  10988. _sapp_macos_lock_mouse(win, lock);
  10989. #elif defined(_SAPP_EMSCRIPTEN)
  10990. _sapp_emsc_lock_mouse(lock);
  10991. #elif defined(_SAPP_WIN32)
  10992. _sapp_win32_lock_mouse(lock);
  10993. #elif defined(_SAPP_LINUX)
  10994. _sapp_x11_lock_mouse(lock);
  10995. #else
  10996. _sapp.mouse.locked = lock;
  10997. #endif
  10998. }
  10999. SOKOL_API_IMPL bool sapp_mouse_locked(void) {
  11000. SOKOL_ASSERT(_sapp.valid);
  11001. // FIXME: is this actually a per-window function??
  11002. const _sapp_window_t* win = _sapp_lookup_window(_sapp.main_window_id);
  11003. SOKOL_ASSERT(win);
  11004. return win->mouse.locked;
  11005. }
  11006. SOKOL_API_IMPL void sapp_set_icon(const sapp_icon_desc* desc) {
  11007. SOKOL_ASSERT(_sapp.valid);
  11008. SOKOL_ASSERT(desc);
  11009. _sapp_set_icon(desc);
  11010. }
  11011. SOKOL_API_IMPL int sapp_width(void) {
  11012. SOKOL_ASSERT(_sapp.valid);
  11013. return _sapp_window_width(_sapp.main_window_id);
  11014. }
  11015. SOKOL_API_IMPL float sapp_widthf(void) {
  11016. SOKOL_ASSERT(_sapp.valid);
  11017. return (float)_sapp_window_width(_sapp.main_window_id);
  11018. }
  11019. SOKOL_API_IMPL int sapp_height(void) {
  11020. SOKOL_ASSERT(_sapp.valid);
  11021. return _sapp_window_height(_sapp.main_window_id);
  11022. }
  11023. SOKOL_API_IMPL float sapp_heightf(void) {
  11024. SOKOL_ASSERT(_sapp.valid);
  11025. return (float)_sapp_window_height(_sapp.main_window_id);
  11026. }
  11027. SOKOL_API_IMPL void sapp_set_client_size(int width, int height) {
  11028. SOKOL_ASSERT(_sapp.valid);
  11029. _sapp_window_set_client_size(_sapp.main_window_id, width, height);
  11030. }
  11031. SOKOL_API_IMPL void sapp_set_client_sizef(float width, float height) {
  11032. SOKOL_ASSERT(_sapp.valid);
  11033. _sapp_window_set_client_size(_sapp.main_window_id, (int)width, (int)height);
  11034. }
  11035. SOKOL_API_IMPL int sapp_client_width(void) {
  11036. SOKOL_ASSERT(_sapp.valid);
  11037. return _sapp_window_client_width(_sapp.main_window_id);
  11038. }
  11039. SOKOL_API_IMPL float sapp_client_widthf(void) {
  11040. SOKOL_ASSERT(_sapp.valid);
  11041. return (float)_sapp_window_client_width(_sapp.main_window_id);
  11042. }
  11043. SOKOL_API_IMPL int sapp_client_height(void) {
  11044. SOKOL_ASSERT(_sapp.valid);
  11045. return _sapp_window_client_height(_sapp.main_window_id);
  11046. }
  11047. SOKOL_API_IMPL float sapp_client_heightf(void) {
  11048. SOKOL_ASSERT(_sapp.valid);
  11049. return (float)_sapp_window_client_height(_sapp.main_window_id);
  11050. }
  11051. SOKOL_API_IMPL void sapp_set_client_pos(int x, int y) {
  11052. SOKOL_ASSERT(_sapp.valid);
  11053. _sapp_window_set_client_pos(_sapp.main_window_id, x, y);
  11054. }
  11055. SOKOL_API_IMPL void sapp_set_client_posf(float x, float y) {
  11056. SOKOL_ASSERT(_sapp.valid);
  11057. _sapp_window_set_client_pos(_sapp.main_window_id, (int)x, (int)y);
  11058. }
  11059. SOKOL_API_IMPL int sapp_client_posx(void) {
  11060. SOKOL_ASSERT(_sapp.valid);
  11061. return _sapp_window_client_posx(_sapp.main_window_id);
  11062. }
  11063. SOKOL_API_IMPL float sapp_client_posxf(void) {
  11064. SOKOL_ASSERT(_sapp.valid);
  11065. return (float)_sapp_window_client_posx(_sapp.main_window_id);
  11066. }
  11067. SOKOL_API_IMPL int sapp_client_posy(void) {
  11068. SOKOL_ASSERT(_sapp.valid);
  11069. return _sapp_window_client_posy(_sapp.main_window_id);
  11070. }
  11071. SOKOL_API_IMPL float sapp_client_posyf(void) {
  11072. SOKOL_ASSERT(_sapp.valid);
  11073. return (float)_sapp_window_client_posy(_sapp.main_window_id);
  11074. }
  11075. SOKOL_API_IMPL int sapp_sample_count(void) {
  11076. SOKOL_ASSERT(_sapp.valid);
  11077. return _sapp_window_sample_count(_sapp.main_window_id);
  11078. }
  11079. SOKOL_API_IMPL bool sapp_high_dpi(void) {
  11080. SOKOL_ASSERT(_sapp.valid);
  11081. return _sapp_window_high_dpi(_sapp.main_window_id);
  11082. }
  11083. SOKOL_API_IMPL float sapp_dpi_scale(void) {
  11084. SOKOL_ASSERT(_sapp.valid);
  11085. return _sapp_window_dpi_scale(_sapp.main_window_id);
  11086. }
  11087. SOKOL_API_IMPL bool sapp_is_fullscreen(void) {
  11088. SOKOL_ASSERT(_sapp.valid);
  11089. return _sapp_window_is_fullscreen(_sapp.main_window_id);
  11090. }
  11091. SOKOL_API_IMPL void sapp_toggle_fullscreen(void) {
  11092. SOKOL_ASSERT(_sapp.valid);
  11093. _sapp_window_toggle_fullscreen(_sapp.main_window_id);
  11094. }
  11095. SOKOL_API_IMPL void sapp_set_title(const char* title) {
  11096. SOKOL_ASSERT(_sapp.valid);
  11097. _sapp_window_set_title(_sapp.main_window_id, title);
  11098. }
  11099. SOKOL_API_IMPL void* sapp_userdata(void) {
  11100. SOKOL_ASSERT(_sapp.valid);
  11101. return _sapp_window_userdata(_sapp.main_window_id);
  11102. }
  11103. SOKOL_API_IMPL sapp_window sapp_open_window(const sapp_window_desc* in_desc) {
  11104. SOKOL_ASSERT(_sapp.valid);
  11105. SOKOL_ASSERT(in_desc);
  11106. const sapp_window_desc desc = _sapp_window_desc_defaults(in_desc);
  11107. return _sapp_make_window_id(_sapp_create_window(&desc));
  11108. }
  11109. SOKOL_API_IMPL void sapp_close_window(sapp_window window) {
  11110. SOKOL_ASSERT(_sapp.valid);
  11111. _sapp_window_t* win = _sapp_lookup_window(window.id);
  11112. if (win) {
  11113. _sapp_platform_close_window(win);
  11114. }
  11115. }
  11116. SOKOL_API_IMPL void sapp_show_window(sapp_window window) {
  11117. SOKOL_ASSERT(_sapp.valid);
  11118. _sapp_window_t* win = _sapp_lookup_window(window.id);
  11119. if (win) {
  11120. _sapp_platform_show_window(win);
  11121. }
  11122. }
  11123. SOKOL_API_IMPL void sapp_hide_window(sapp_window window) {
  11124. SOKOL_ASSERT(_sapp.valid);
  11125. _sapp_window_t* win = _sapp_lookup_window(window.id);
  11126. if (win) {
  11127. _sapp_platform_hide_window(win);
  11128. }
  11129. }
  11130. SOKOL_API_IMPL bool sapp_window_visible(sapp_window window) {
  11131. SOKOL_ASSERT(_sapp.valid);
  11132. _sapp_window_t* win = _sapp_lookup_window(window.id);
  11133. if (win) {
  11134. return _sapp_platform_window_visible(win);
  11135. }
  11136. else {
  11137. return true;
  11138. }
  11139. }
  11140. SOKOL_API_IMPL void sapp_focus_window(sapp_window window) {
  11141. SOKOL_ASSERT(_sapp.valid);
  11142. _sapp_window_t* win = _sapp_lookup_window(window.id);
  11143. if (win) {
  11144. _sapp_platform_focus_window(win);
  11145. }
  11146. }
  11147. SOKOL_API_IMPL bool sapp_window_focused(sapp_window window) {
  11148. SOKOL_ASSERT(_sapp.valid);
  11149. _sapp_window_t* win = _sapp_lookup_window(window.id);
  11150. if (win) {
  11151. return _sapp_platform_window_focused(win);
  11152. }
  11153. else {
  11154. return false;
  11155. }
  11156. }
  11157. SOKOL_API_IMPL bool sapp_window_minimized(sapp_window window) {
  11158. SOKOL_ASSERT(_sapp.valid);
  11159. _sapp_window_t* win = _sapp_lookup_window(window.id);
  11160. if (win) {
  11161. return _sapp_platform_window_minimized(win);
  11162. }
  11163. else {
  11164. return false;
  11165. }
  11166. }
  11167. SOKOL_API_IMPL void sapp_activate_window_context(sapp_window window) {
  11168. SOKOL_ASSERT(_sapp.valid);
  11169. _sapp_window_t* win = _sapp_lookup_window(window.id);
  11170. if (win) {
  11171. _sapp_platform_activate_window_context(win);
  11172. }
  11173. }
  11174. SOKOL_API_IMPL int sapp_window_slot_index(sapp_window window) {
  11175. SOKOL_ASSERT(_sapp.valid);
  11176. SOKOL_ASSERT(0 != _sapp_lookup_window(window.id));
  11177. int slot_index = _sapp_slot_index(window.id);
  11178. SOKOL_ASSERT(slot_index >= 1);
  11179. return slot_index - 1;
  11180. }
  11181. SOKOL_API_IMPL sapp_window sapp_main_window(void) {
  11182. SOKOL_ASSERT(_sapp.valid);
  11183. return _sapp_make_window_id(_sapp.main_window_id);
  11184. }
  11185. SOKOL_API_IMPL sapp_window sapp_first_window(void) {
  11186. SOKOL_ASSERT(_sapp.valid);
  11187. return _sapp_make_window_id(_sapp.main_window_id);
  11188. }
  11189. SOKOL_API_IMPL sapp_window sapp_next_window(sapp_window window) {
  11190. SOKOL_ASSERT(_sapp.valid);
  11191. for (int i = _sapp_slot_index(window.id) + 1; i < _sapp.window_pool.pool.size; i++) {
  11192. uint32_t win_id = _sapp.window_pool.windows[i].slot.id;
  11193. if (0 != _sapp_lookup_window(win_id)) {
  11194. return _sapp_make_window_id(win_id);
  11195. }
  11196. }
  11197. return _sapp_make_window_id(SAPP_INVALID_ID);
  11198. }
  11199. SOKOL_API_IMPL bool sapp_window_valid(sapp_window window) {
  11200. SOKOL_ASSERT(_sapp.valid);
  11201. return 0 != _sapp_lookup_window(window.id);
  11202. }
  11203. SOKOL_API_IMPL int sapp_window_width(sapp_window window) {
  11204. SOKOL_ASSERT(_sapp.valid);
  11205. return _sapp_window_width(window.id);
  11206. }
  11207. SOKOL_API_IMPL float sapp_window_widthf(sapp_window window) {
  11208. SOKOL_ASSERT(_sapp.valid);
  11209. return (float)_sapp_window_width(window.id);
  11210. }
  11211. SOKOL_API_IMPL int sapp_window_height(sapp_window window) {
  11212. SOKOL_ASSERT(_sapp.valid);
  11213. return _sapp_window_height(window.id);
  11214. }
  11215. SOKOL_API_IMPL float sapp_window_heightf(sapp_window window) {
  11216. SOKOL_ASSERT(_sapp.valid);
  11217. return (float)_sapp_window_height(window.id);
  11218. }
  11219. SOKOL_API_IMPL void sapp_window_set_client_size(sapp_window window, int width, int height) {
  11220. SOKOL_ASSERT(_sapp.valid);
  11221. _sapp_window_set_client_size(window.id, width, height);
  11222. }
  11223. SOKOL_API_IMPL void sapp_window_set_client_sizef(sapp_window window, float width, float height) {
  11224. SOKOL_ASSERT(_sapp.valid);
  11225. _sapp_window_set_client_size(window.id, (int)width, (int)height);
  11226. }
  11227. SOKOL_API_IMPL int sapp_window_client_width(sapp_window window) {
  11228. SOKOL_ASSERT(_sapp.valid);
  11229. return _sapp_window_client_width(window.id);
  11230. }
  11231. SOKOL_API_IMPL float sapp_window_client_widthf(sapp_window window) {
  11232. SOKOL_ASSERT(_sapp.valid);
  11233. return (float)_sapp_window_client_width(window.id);
  11234. }
  11235. SOKOL_API_IMPL int sapp_window_client_height(sapp_window window) {
  11236. SOKOL_ASSERT(_sapp.valid);
  11237. return _sapp_window_client_height(window.id);
  11238. }
  11239. SOKOL_API_IMPL float sapp_window_client_heightf(sapp_window window) {
  11240. SOKOL_ASSERT(_sapp.valid);
  11241. return (float)_sapp_window_client_height(window.id);
  11242. }
  11243. SOKOL_API_IMPL void sapp_window_set_client_pos(sapp_window window, int x, int y) {
  11244. SOKOL_ASSERT(_sapp.valid);
  11245. _sapp_window_set_client_pos(window.id, x, y);
  11246. }
  11247. SOKOL_API_IMPL void sapp_window_set_client_posf(sapp_window window, float x, float y) {
  11248. SOKOL_ASSERT(_sapp.valid);
  11249. _sapp_window_set_client_pos(window.id, (int)x, (int)y);
  11250. }
  11251. SOKOL_API_IMPL int sapp_window_client_posx(sapp_window window) {
  11252. SOKOL_ASSERT(_sapp.valid);
  11253. return _sapp_window_client_posx(window.id);
  11254. }
  11255. SOKOL_API_IMPL float sapp_window_client_posxf(sapp_window window) {
  11256. SOKOL_ASSERT(_sapp.valid);
  11257. return (float)_sapp_window_client_posx(window.id);
  11258. }
  11259. SOKOL_API_IMPL int sapp_window_client_posy(sapp_window window) {
  11260. SOKOL_ASSERT(_sapp.valid);
  11261. return _sapp_window_client_posy(window.id);
  11262. }
  11263. SOKOL_API_IMPL float sapp_window_client_posyf(sapp_window window) {
  11264. SOKOL_ASSERT(_sapp.valid);
  11265. return (float)_sapp_window_client_posy(window.id);
  11266. }
  11267. SOKOL_API_IMPL int sapp_window_sample_count(sapp_window window) {
  11268. SOKOL_ASSERT(_sapp.valid);
  11269. return _sapp_window_sample_count(window.id);
  11270. }
  11271. SOKOL_API_IMPL bool sapp_window_high_dpi(sapp_window window) {
  11272. SOKOL_ASSERT(_sapp.valid);
  11273. return _sapp_window_high_dpi(window.id);
  11274. }
  11275. SOKOL_API_IMPL float sapp_window_dpi_scale(sapp_window window) {
  11276. SOKOL_ASSERT(_sapp.valid);
  11277. return _sapp_window_dpi_scale(window.id);
  11278. }
  11279. SOKOL_API_IMPL bool sapp_window_is_fullscreen(sapp_window window) {
  11280. SOKOL_ASSERT(_sapp.valid);
  11281. return _sapp_window_is_fullscreen(window.id);
  11282. }
  11283. SOKOL_API_IMPL void sapp_window_toggle_fullscreen(sapp_window window) {
  11284. SOKOL_ASSERT(_sapp.valid);
  11285. _sapp_window_toggle_fullscreen(window.id);
  11286. }
  11287. SOKOL_API_IMPL void sapp_window_set_title(sapp_window window, const char* title) {
  11288. SOKOL_ASSERT(_sapp.valid);
  11289. _sapp_window_set_title(window.id, title);
  11290. }
  11291. SOKOL_API_IMPL void* sapp_window_userdata(sapp_window window) {
  11292. SOKOL_ASSERT(_sapp.valid);
  11293. return _sapp_window_userdata(window.id);
  11294. }
  11295. /* NOTE: on HTML5, sapp_set_clipboard_string() must be called from within event handler! */
  11296. SOKOL_API_IMPL void sapp_set_clipboard_string(const char* str) {
  11297. SOKOL_ASSERT(_sapp.valid);
  11298. if (!_sapp.clipboard.enabled) {
  11299. return;
  11300. }
  11301. SOKOL_ASSERT(str);
  11302. #if defined(_SAPP_MACOS)
  11303. _sapp_macos_set_clipboard_string(str);
  11304. #elif defined(_SAPP_EMSCRIPTEN)
  11305. _sapp_emsc_set_clipboard_string(str);
  11306. #elif defined(_SAPP_WIN32)
  11307. _sapp_win32_set_clipboard_string(str);
  11308. #else
  11309. /* not implemented */
  11310. #endif
  11311. _sapp_strcpy(str, _sapp.clipboard.buffer, _sapp.clipboard.buf_size);
  11312. }
  11313. SOKOL_API_IMPL const char* sapp_get_clipboard_string(void) {
  11314. SOKOL_ASSERT(_sapp.valid);
  11315. if (!_sapp.clipboard.enabled) {
  11316. return "";
  11317. }
  11318. #if defined(_SAPP_MACOS)
  11319. return _sapp_macos_get_clipboard_string();
  11320. #elif defined(_SAPP_EMSCRIPTEN)
  11321. return _sapp.clipboard.buffer;
  11322. #elif defined(_SAPP_WIN32)
  11323. return _sapp_win32_get_clipboard_string();
  11324. #else
  11325. /* not implemented */
  11326. return _sapp.cur_window->clipboard.buffer;
  11327. #endif
  11328. }
  11329. SOKOL_API_IMPL int sapp_get_num_dropped_files(void) {
  11330. SOKOL_ASSERT(_sapp.valid);
  11331. return _sapp.drop.num_files;
  11332. }
  11333. SOKOL_API_IMPL const char* sapp_get_dropped_file_path(int index) {
  11334. SOKOL_ASSERT(_sapp.valid);
  11335. SOKOL_ASSERT((index >= 0) && (index < _sapp.drop.num_files));
  11336. if (!_sapp.drop.enabled) {
  11337. return "";
  11338. }
  11339. if ((index < 0) || (index >= _sapp.drop.max_files)) {
  11340. return "";
  11341. }
  11342. return (const char*) _sapp_dropped_file_path_ptr(index);
  11343. }
  11344. SOKOL_API_IMPL bool sapp_gles2(void) {
  11345. SOKOL_ASSERT(_sapp.valid);
  11346. return _sapp.gles2_fallback;
  11347. }
  11348. SOKOL_API_IMPL uint32_t sapp_html5_get_dropped_file_size(int index) {
  11349. SOKOL_ASSERT(_sapp.valid);
  11350. SOKOL_ASSERT((index >= 0) && (index < _sapp.drop.num_files));
  11351. #if defined(_SAPP_EMSCRIPTEN)
  11352. if (!_sapp.drop.enabled) {
  11353. return 0;
  11354. }
  11355. return sapp_js_dropped_file_size(index);
  11356. #else
  11357. _SOKOL_UNUSED(index);
  11358. return 0;
  11359. #endif
  11360. }
  11361. SOKOL_API_IMPL void sapp_html5_fetch_dropped_file(const sapp_html5_fetch_request* request) {
  11362. SOKOL_ASSERT(_sapp.valid);
  11363. SOKOL_ASSERT(request);
  11364. SOKOL_ASSERT(request->callback);
  11365. SOKOL_ASSERT(request->buffer_ptr);
  11366. SOKOL_ASSERT(request->buffer_size > 0);
  11367. if (!_sapp.drop.enabled) {
  11368. return;
  11369. }
  11370. #if defined(_SAPP_EMSCRIPTEN)
  11371. const int index = request->dropped_file_index;
  11372. sapp_html5_fetch_error error_code = SAPP_HTML5_FETCH_ERROR_NO_ERROR;
  11373. if ((index < 0) || (index >= _sapp.drop.num_files)) {
  11374. error_code = SAPP_HTML5_FETCH_ERROR_OTHER;
  11375. }
  11376. if (sapp_html5_get_dropped_file_size(index) > request->buffer_size) {
  11377. error_code = SAPP_HTML5_FETCH_ERROR_BUFFER_TOO_SMALL;
  11378. }
  11379. if (SAPP_HTML5_FETCH_ERROR_NO_ERROR != error_code) {
  11380. _sapp_emsc_invoke_fetch_cb(index,
  11381. false, // success
  11382. (int)error_code,
  11383. request->callback,
  11384. 0, // fetched_size
  11385. request->buffer_ptr,
  11386. request->buffer_size,
  11387. request->user_data);
  11388. }
  11389. else {
  11390. sapp_js_fetch_dropped_file(index,
  11391. request->callback,
  11392. request->buffer_ptr,
  11393. request->buffer_size,
  11394. request->user_data);
  11395. }
  11396. #else
  11397. _SOKOL_UNUSED(request);
  11398. #endif
  11399. }
  11400. SOKOL_API_IMPL const void* sapp_metal_get_device(void) {
  11401. SOKOL_ASSERT(_sapp.valid);
  11402. #if defined(SOKOL_METAL)
  11403. #if defined(_SAPP_MACOS)
  11404. const void* obj = (__bridge const void*) _sapp.macos.mtl_device;
  11405. #else
  11406. const void* obj = (__bridge const void*) _sapp.ios.mtl_device;
  11407. #endif
  11408. SOKOL_ASSERT(obj);
  11409. return obj;
  11410. #else
  11411. return 0;
  11412. #endif
  11413. }
  11414. SOKOL_API_IMPL const void* sapp_metal_get_window_renderpass_descriptor(sapp_window window) {
  11415. SOKOL_ASSERT(_sapp.valid);
  11416. #if defined(SOKOL_METAL)
  11417. const _sapp_window_t* win = _sapp_lookup_window(window.id);
  11418. if (win) {
  11419. #if defined(_SAPP_MACOS)
  11420. const void* obj = (__bridge const void*) [win->macos.view currentRenderPassDescriptor];
  11421. #else
  11422. const void* obj = (__bridge const void*) [_sapp.window.ios.view currentRenderPassDescriptor];
  11423. #endif
  11424. SOKOL_ASSERT(obj);
  11425. return obj;
  11426. }
  11427. else {
  11428. return 0;
  11429. }
  11430. #else
  11431. _SOKOL_UNUSED(window);
  11432. return 0;
  11433. #endif
  11434. }
  11435. SOKOL_API_IMPL const void* sapp_metal_get_renderpass_descriptor(void) {
  11436. SOKOL_ASSERT(_sapp.valid);
  11437. return sapp_metal_get_window_renderpass_descriptor(sapp_main_window());
  11438. }
  11439. SOKOL_API_IMPL const void* sapp_metal_get_window_drawable(sapp_window window) {
  11440. #if defined(SOKOL_METAL)
  11441. const _sapp_window_t* win = _sapp_lookup_window(window.id);
  11442. if (win) {
  11443. #if defined(_SAPP_MACOS)
  11444. const void* obj = (__bridge const void*) [win->macos.view currentDrawable];
  11445. #else
  11446. const void* obj = (__bridge const void*) [_sapp.window.ios.view currentDrawable];
  11447. #endif
  11448. SOKOL_ASSERT(obj);
  11449. return obj;
  11450. }
  11451. else {
  11452. return 0;
  11453. }
  11454. #else
  11455. _SOKOL_UNUSED(window);
  11456. return 0;
  11457. #endif
  11458. }
  11459. SOKOL_API_IMPL const void* sapp_metal_get_drawable(void) {
  11460. return sapp_metal_get_window_drawable(sapp_main_window());
  11461. }
  11462. SOKOL_API_IMPL const void* sapp_macos_get_nswindow(sapp_window window) {
  11463. SOKOL_ASSERT(_sapp.valid);
  11464. #if defined(_SAPP_MACOS)
  11465. const _sapp_window_t* win = _sapp_lookup_window(window.id);
  11466. if (win) {
  11467. const void* obj = (__bridge const void*) win->macos.window;
  11468. SOKOL_ASSERT(obj);
  11469. return obj;
  11470. }
  11471. else {
  11472. return 0;
  11473. }
  11474. #else
  11475. _SOKOL_UNUSED(win);
  11476. return 0;
  11477. #endif
  11478. }
  11479. SOKOL_API_IMPL const void* sapp_ios_get_window(void) {
  11480. SOKOL_ASSERT(_sapp.valid);
  11481. #if defined(_SAPP_IOS)
  11482. const void* obj = (__bridge const void*) _sapp.ios.window;
  11483. SOKOL_ASSERT(obj);
  11484. return obj;
  11485. #else
  11486. return 0;
  11487. #endif
  11488. }
  11489. SOKOL_API_IMPL const void* sapp_d3d11_get_device(void) {
  11490. SOKOL_ASSERT(_sapp.valid);
  11491. #if defined(SOKOL_D3D11)
  11492. return _sapp.d3d11.device;
  11493. #else
  11494. return 0;
  11495. #endif
  11496. }
  11497. SOKOL_API_IMPL const void* sapp_d3d11_get_device_context(void) {
  11498. SOKOL_ASSERT(_sapp.valid);
  11499. #if defined(SOKOL_D3D11)
  11500. return _sapp.d3d11.device_context;
  11501. #else
  11502. return 0;
  11503. #endif
  11504. }
  11505. SOKOL_API_IMPL const void* sapp_d3d11_get_swap_chain(void) {
  11506. SOKOL_ASSERT(_sapp.valid);
  11507. #if defined(SOKOL_D3D11)
  11508. return _sapp.d3d11.swap_chain;
  11509. #else
  11510. return 0;
  11511. #endif
  11512. }
  11513. SOKOL_API_IMPL const void* sapp_d3d11_get_render_target_view(void) {
  11514. SOKOL_ASSERT(_sapp.valid);
  11515. #if defined(SOKOL_D3D11)
  11516. if (_sapp.d3d11.msaa_rtv) {
  11517. return _sapp.d3d11.msaa_rtv;
  11518. }
  11519. else {
  11520. return _sapp.d3d11.rtv;
  11521. }
  11522. #else
  11523. return 0;
  11524. #endif
  11525. }
  11526. SOKOL_API_IMPL const void* sapp_d3d11_get_depth_stencil_view(void) {
  11527. SOKOL_ASSERT(_sapp.valid);
  11528. #if defined(SOKOL_D3D11)
  11529. return _sapp.d3d11.dsv;
  11530. #else
  11531. return 0;
  11532. #endif
  11533. }
  11534. SOKOL_API_IMPL const void* sapp_win32_get_hwnd(void) {
  11535. SOKOL_ASSERT(_sapp.valid);
  11536. #if defined(_SAPP_WIN32)
  11537. return _sapp.win32.hwnd;
  11538. #else
  11539. return 0;
  11540. #endif
  11541. }
  11542. SOKOL_API_IMPL const void* sapp_wgpu_get_device(void) {
  11543. SOKOL_ASSERT(_sapp.valid);
  11544. #if defined(_SAPP_EMSCRIPTEN) && defined(SOKOL_WGPU)
  11545. return (const void*) _sapp.emsc.wgpu.device;
  11546. #else
  11547. return 0;
  11548. #endif
  11549. }
  11550. SOKOL_API_IMPL const void* sapp_wgpu_get_render_view(void) {
  11551. SOKOL_ASSERT(_sapp.valid);
  11552. #if defined(_SAPP_EMSCRIPTEN) && defined(SOKOL_WGPU)
  11553. if (_sapp.sample_count > 1) {
  11554. return (const void*) _sapp.emsc.wgpu.msaa_view;
  11555. }
  11556. else {
  11557. return (const void*) _sapp.emsc.wgpu.swapchain_view;
  11558. }
  11559. #else
  11560. return 0;
  11561. #endif
  11562. }
  11563. SOKOL_API_IMPL const void* sapp_wgpu_get_resolve_view(void) {
  11564. SOKOL_ASSERT(_sapp.valid);
  11565. #if defined(_SAPP_EMSCRIPTEN) && defined(SOKOL_WGPU)
  11566. if (_sapp.sample_count > 1) {
  11567. return (const void*) _sapp.emsc.wgpu.swapchain_view;
  11568. }
  11569. else {
  11570. return 0;
  11571. }
  11572. #else
  11573. return 0;
  11574. #endif
  11575. }
  11576. SOKOL_API_IMPL const void* sapp_wgpu_get_depth_stencil_view(void) {
  11577. SOKOL_ASSERT(_sapp.valid);
  11578. #if defined(_SAPP_EMSCRIPTEN) && defined(SOKOL_WGPU)
  11579. return (const void*) _sapp.emsc.wgpu.depth_stencil_view;
  11580. #else
  11581. return 0;
  11582. #endif
  11583. }
  11584. SOKOL_API_IMPL const void* sapp_android_get_native_activity(void) {
  11585. SOKOL_ASSERT(_sapp.valid);
  11586. #if defined(_SAPP_ANDROID)
  11587. return (void*)_sapp.android.activity;
  11588. #else
  11589. return 0;
  11590. #endif
  11591. }
  11592. SOKOL_API_IMPL void sapp_html5_ask_leave_site(bool ask) {
  11593. SOKOL_ASSERT(_sapp.valid);
  11594. #if defined(_SAPP_EMSCRIPTEN)
  11595. _sapp.emsc.ask_leave_site = ask;
  11596. #else
  11597. _SOKOL_UNUSED(ask);
  11598. #endif
  11599. }
  11600. #endif /* SOKOL_APP_IMPL */