sokol_app.h 545 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676367736783679368036813682368336843685368636873688368936903691369236933694369536963697369836993700370137023703370437053706370737083709371037113712371337143715371637173718371937203721372237233724372537263727372837293730373137323733373437353736373737383739374037413742374337443745374637473748374937503751375237533754375537563757375837593760376137623763376437653766376737683769377037713772377337743775377637773778377937803781378237833784378537863787378837893790379137923793379437953796379737983799380038013802380338043805380638073808380938103811381238133814381538163817381838193820382138223823382438253826382738283829383038313832383338343835383638373838383938403841384238433844384538463847384838493850385138523853385438553856385738583859386038613862386338643865386638673868386938703871387238733874387538763877387838793880388138823883388438853886388738883889389038913892389338943895389638973898389939003901390239033904390539063907390839093910391139123913391439153916391739183919392039213922392339243925392639273928392939303931393239333934393539363937393839393940394139423943394439453946394739483949395039513952395339543955395639573958395939603961396239633964396539663967396839693970397139723973397439753976397739783979398039813982398339843985398639873988398939903991399239933994399539963997399839994000400140024003400440054006400740084009401040114012401340144015401640174018401940204021402240234024402540264027402840294030403140324033403440354036403740384039404040414042404340444045404640474048404940504051405240534054405540564057405840594060406140624063406440654066406740684069407040714072407340744075407640774078407940804081408240834084408540864087408840894090409140924093409440954096409740984099410041014102410341044105410641074108410941104111411241134114411541164117411841194120412141224123412441254126412741284129413041314132413341344135413641374138413941404141414241434144414541464147414841494150415141524153415441554156415741584159416041614162416341644165416641674168416941704171417241734174417541764177417841794180418141824183418441854186418741884189419041914192419341944195419641974198419942004201420242034204420542064207420842094210421142124213421442154216421742184219422042214222422342244225422642274228422942304231423242334234423542364237423842394240424142424243424442454246424742484249425042514252425342544255425642574258425942604261426242634264426542664267426842694270427142724273427442754276427742784279428042814282428342844285428642874288428942904291429242934294429542964297429842994300430143024303430443054306430743084309431043114312431343144315431643174318431943204321432243234324432543264327432843294330433143324333433443354336433743384339434043414342434343444345434643474348434943504351435243534354435543564357435843594360436143624363436443654366436743684369437043714372437343744375437643774378437943804381438243834384438543864387438843894390439143924393439443954396439743984399440044014402440344044405440644074408440944104411441244134414441544164417441844194420442144224423442444254426442744284429443044314432443344344435443644374438443944404441444244434444444544464447444844494450445144524453445444554456445744584459446044614462446344644465446644674468446944704471447244734474447544764477447844794480448144824483448444854486448744884489449044914492449344944495449644974498449945004501450245034504450545064507450845094510451145124513451445154516451745184519452045214522452345244525452645274528452945304531453245334534453545364537453845394540454145424543454445454546454745484549455045514552455345544555455645574558455945604561456245634564456545664567456845694570457145724573457445754576457745784579458045814582458345844585458645874588458945904591459245934594459545964597459845994600460146024603460446054606460746084609461046114612461346144615461646174618461946204621462246234624462546264627462846294630463146324633463446354636463746384639464046414642464346444645464646474648464946504651465246534654465546564657465846594660466146624663466446654666466746684669467046714672467346744675467646774678467946804681468246834684468546864687468846894690469146924693469446954696469746984699470047014702470347044705470647074708470947104711471247134714471547164717471847194720472147224723472447254726472747284729473047314732473347344735473647374738473947404741474247434744474547464747474847494750475147524753475447554756475747584759476047614762476347644765476647674768476947704771477247734774477547764777477847794780478147824783478447854786478747884789479047914792479347944795479647974798479948004801480248034804480548064807480848094810481148124813481448154816481748184819482048214822482348244825482648274828482948304831483248334834483548364837483848394840484148424843484448454846484748484849485048514852485348544855485648574858485948604861486248634864486548664867486848694870487148724873487448754876487748784879488048814882488348844885488648874888488948904891489248934894489548964897489848994900490149024903490449054906490749084909491049114912491349144915491649174918491949204921492249234924492549264927492849294930493149324933493449354936493749384939494049414942494349444945494649474948494949504951495249534954495549564957495849594960496149624963496449654966496749684969497049714972497349744975497649774978497949804981498249834984498549864987498849894990499149924993499449954996499749984999500050015002500350045005500650075008500950105011501250135014501550165017501850195020502150225023502450255026502750285029503050315032503350345035503650375038503950405041504250435044504550465047504850495050505150525053505450555056505750585059506050615062506350645065506650675068506950705071507250735074507550765077507850795080508150825083508450855086508750885089509050915092509350945095509650975098509951005101510251035104510551065107510851095110511151125113511451155116511751185119512051215122512351245125512651275128512951305131513251335134513551365137513851395140514151425143514451455146514751485149515051515152515351545155515651575158515951605161516251635164516551665167516851695170517151725173517451755176517751785179518051815182518351845185518651875188518951905191519251935194519551965197519851995200520152025203520452055206520752085209521052115212521352145215521652175218521952205221522252235224522552265227522852295230523152325233523452355236523752385239524052415242524352445245524652475248524952505251525252535254525552565257525852595260526152625263526452655266526752685269527052715272527352745275527652775278527952805281528252835284528552865287528852895290529152925293529452955296529752985299530053015302530353045305530653075308530953105311531253135314531553165317531853195320532153225323532453255326532753285329533053315332533353345335533653375338533953405341534253435344534553465347534853495350535153525353535453555356535753585359536053615362536353645365536653675368536953705371537253735374537553765377537853795380538153825383538453855386538753885389539053915392539353945395539653975398539954005401540254035404540554065407540854095410541154125413541454155416541754185419542054215422542354245425542654275428542954305431543254335434543554365437543854395440544154425443544454455446544754485449545054515452545354545455545654575458545954605461546254635464546554665467546854695470547154725473547454755476547754785479548054815482548354845485548654875488548954905491549254935494549554965497549854995500550155025503550455055506550755085509551055115512551355145515551655175518551955205521552255235524552555265527552855295530553155325533553455355536553755385539554055415542554355445545554655475548554955505551555255535554555555565557555855595560556155625563556455655566556755685569557055715572557355745575557655775578557955805581558255835584558555865587558855895590559155925593559455955596559755985599560056015602560356045605560656075608560956105611561256135614561556165617561856195620562156225623562456255626562756285629563056315632563356345635563656375638563956405641564256435644564556465647564856495650565156525653565456555656565756585659566056615662566356645665566656675668566956705671567256735674567556765677567856795680568156825683568456855686568756885689569056915692569356945695569656975698569957005701570257035704570557065707570857095710571157125713571457155716571757185719572057215722572357245725572657275728572957305731573257335734573557365737573857395740574157425743574457455746574757485749575057515752575357545755575657575758575957605761576257635764576557665767576857695770577157725773577457755776577757785779578057815782578357845785578657875788578957905791579257935794579557965797579857995800580158025803580458055806580758085809581058115812581358145815581658175818581958205821582258235824582558265827582858295830583158325833583458355836583758385839584058415842584358445845584658475848584958505851585258535854585558565857585858595860586158625863586458655866586758685869587058715872587358745875587658775878587958805881588258835884588558865887588858895890589158925893589458955896589758985899590059015902590359045905590659075908590959105911591259135914591559165917591859195920592159225923592459255926592759285929593059315932593359345935593659375938593959405941594259435944594559465947594859495950595159525953595459555956595759585959596059615962596359645965596659675968596959705971597259735974597559765977597859795980598159825983598459855986598759885989599059915992599359945995599659975998599960006001600260036004600560066007600860096010601160126013601460156016601760186019602060216022602360246025602660276028602960306031603260336034603560366037603860396040604160426043604460456046604760486049605060516052605360546055605660576058605960606061606260636064606560666067606860696070607160726073607460756076607760786079608060816082608360846085608660876088608960906091609260936094609560966097609860996100610161026103610461056106610761086109611061116112611361146115611661176118611961206121612261236124612561266127612861296130613161326133613461356136613761386139614061416142614361446145614661476148614961506151615261536154615561566157615861596160616161626163616461656166616761686169617061716172617361746175617661776178617961806181618261836184618561866187618861896190619161926193619461956196619761986199620062016202620362046205620662076208620962106211621262136214621562166217621862196220622162226223622462256226622762286229623062316232623362346235623662376238623962406241624262436244624562466247624862496250625162526253625462556256625762586259626062616262626362646265626662676268626962706271627262736274627562766277627862796280628162826283628462856286628762886289629062916292629362946295629662976298629963006301630263036304630563066307630863096310631163126313631463156316631763186319632063216322632363246325632663276328632963306331633263336334633563366337633863396340634163426343634463456346634763486349635063516352635363546355635663576358635963606361636263636364636563666367636863696370637163726373637463756376637763786379638063816382638363846385638663876388638963906391639263936394639563966397639863996400640164026403640464056406640764086409641064116412641364146415641664176418641964206421642264236424642564266427642864296430643164326433643464356436643764386439644064416442644364446445644664476448644964506451645264536454645564566457645864596460646164626463646464656466646764686469647064716472647364746475647664776478647964806481648264836484648564866487648864896490649164926493649464956496649764986499650065016502650365046505650665076508650965106511651265136514651565166517651865196520652165226523652465256526652765286529653065316532653365346535653665376538653965406541654265436544654565466547654865496550655165526553655465556556655765586559656065616562656365646565656665676568656965706571657265736574657565766577657865796580658165826583658465856586658765886589659065916592659365946595659665976598659966006601660266036604660566066607660866096610661166126613661466156616661766186619662066216622662366246625662666276628662966306631663266336634663566366637663866396640664166426643664466456646664766486649665066516652665366546655665666576658665966606661666266636664666566666667666866696670667166726673667466756676667766786679668066816682668366846685668666876688668966906691669266936694669566966697669866996700670167026703670467056706670767086709671067116712671367146715671667176718671967206721672267236724672567266727672867296730673167326733673467356736673767386739674067416742674367446745674667476748674967506751675267536754675567566757675867596760676167626763676467656766676767686769677067716772677367746775677667776778677967806781678267836784678567866787678867896790679167926793679467956796679767986799680068016802680368046805680668076808680968106811681268136814681568166817681868196820682168226823682468256826682768286829683068316832683368346835683668376838683968406841684268436844684568466847684868496850685168526853685468556856685768586859686068616862686368646865686668676868686968706871687268736874687568766877687868796880688168826883688468856886688768886889689068916892689368946895689668976898689969006901690269036904690569066907690869096910691169126913691469156916691769186919692069216922692369246925692669276928692969306931693269336934693569366937693869396940694169426943694469456946694769486949695069516952695369546955695669576958695969606961696269636964696569666967696869696970697169726973697469756976697769786979698069816982698369846985698669876988698969906991699269936994699569966997699869997000700170027003700470057006700770087009701070117012701370147015701670177018701970207021702270237024702570267027702870297030703170327033703470357036703770387039704070417042704370447045704670477048704970507051705270537054705570567057705870597060706170627063706470657066706770687069707070717072707370747075707670777078707970807081708270837084708570867087708870897090709170927093709470957096709770987099710071017102710371047105710671077108710971107111711271137114711571167117711871197120712171227123712471257126712771287129713071317132713371347135713671377138713971407141714271437144714571467147714871497150715171527153715471557156715771587159716071617162716371647165716671677168716971707171717271737174717571767177717871797180718171827183718471857186718771887189719071917192719371947195719671977198719972007201720272037204720572067207720872097210721172127213721472157216721772187219722072217222722372247225722672277228722972307231723272337234723572367237723872397240724172427243724472457246724772487249725072517252725372547255725672577258725972607261726272637264726572667267726872697270727172727273727472757276727772787279728072817282728372847285728672877288728972907291729272937294729572967297729872997300730173027303730473057306730773087309731073117312731373147315731673177318731973207321732273237324732573267327732873297330733173327333733473357336733773387339734073417342734373447345734673477348734973507351735273537354735573567357735873597360736173627363736473657366736773687369737073717372737373747375737673777378737973807381738273837384738573867387738873897390739173927393739473957396739773987399740074017402740374047405740674077408740974107411741274137414741574167417741874197420742174227423742474257426742774287429743074317432743374347435743674377438743974407441744274437444744574467447744874497450745174527453745474557456745774587459746074617462746374647465746674677468746974707471747274737474747574767477747874797480748174827483748474857486748774887489749074917492749374947495749674977498749975007501750275037504750575067507750875097510751175127513751475157516751775187519752075217522752375247525752675277528752975307531753275337534753575367537753875397540754175427543754475457546754775487549755075517552755375547555755675577558755975607561756275637564756575667567756875697570757175727573757475757576757775787579758075817582758375847585758675877588758975907591759275937594759575967597759875997600760176027603760476057606760776087609761076117612761376147615761676177618761976207621762276237624762576267627762876297630763176327633763476357636763776387639764076417642764376447645764676477648764976507651765276537654765576567657765876597660766176627663766476657666766776687669767076717672767376747675767676777678767976807681768276837684768576867687768876897690769176927693769476957696769776987699770077017702770377047705770677077708770977107711771277137714771577167717771877197720772177227723772477257726772777287729773077317732773377347735773677377738773977407741774277437744774577467747774877497750775177527753775477557756775777587759776077617762776377647765776677677768776977707771777277737774777577767777777877797780778177827783778477857786778777887789779077917792779377947795779677977798779978007801780278037804780578067807780878097810781178127813781478157816781778187819782078217822782378247825782678277828782978307831783278337834783578367837783878397840784178427843784478457846784778487849785078517852785378547855785678577858785978607861786278637864786578667867786878697870787178727873787478757876787778787879788078817882788378847885788678877888788978907891789278937894789578967897789878997900790179027903790479057906790779087909791079117912791379147915791679177918791979207921792279237924792579267927792879297930793179327933793479357936793779387939794079417942794379447945794679477948794979507951795279537954795579567957795879597960796179627963796479657966796779687969797079717972797379747975797679777978797979807981798279837984798579867987798879897990799179927993799479957996799779987999800080018002800380048005800680078008800980108011801280138014801580168017801880198020802180228023802480258026802780288029803080318032803380348035803680378038803980408041804280438044804580468047804880498050805180528053805480558056805780588059806080618062806380648065806680678068806980708071807280738074807580768077807880798080808180828083808480858086808780888089809080918092809380948095809680978098809981008101810281038104810581068107810881098110811181128113811481158116811781188119812081218122812381248125812681278128812981308131813281338134813581368137813881398140814181428143814481458146814781488149815081518152815381548155815681578158815981608161816281638164816581668167816881698170817181728173817481758176817781788179818081818182818381848185818681878188818981908191819281938194819581968197819881998200820182028203820482058206820782088209821082118212821382148215821682178218821982208221822282238224822582268227822882298230823182328233823482358236823782388239824082418242824382448245824682478248824982508251825282538254825582568257825882598260826182628263826482658266826782688269827082718272827382748275827682778278827982808281828282838284828582868287828882898290829182928293829482958296829782988299830083018302830383048305830683078308830983108311831283138314831583168317831883198320832183228323832483258326832783288329833083318332833383348335833683378338833983408341834283438344834583468347834883498350835183528353835483558356835783588359836083618362836383648365836683678368836983708371837283738374837583768377837883798380838183828383838483858386838783888389839083918392839383948395839683978398839984008401840284038404840584068407840884098410841184128413841484158416841784188419842084218422842384248425842684278428842984308431843284338434843584368437843884398440844184428443844484458446844784488449845084518452845384548455845684578458845984608461846284638464846584668467846884698470847184728473847484758476847784788479848084818482848384848485848684878488848984908491849284938494849584968497849884998500850185028503850485058506850785088509851085118512851385148515851685178518851985208521852285238524852585268527852885298530853185328533853485358536853785388539854085418542854385448545854685478548854985508551855285538554855585568557855885598560856185628563856485658566856785688569857085718572857385748575857685778578857985808581858285838584858585868587858885898590859185928593859485958596859785988599860086018602860386048605860686078608860986108611861286138614861586168617861886198620862186228623862486258626862786288629863086318632863386348635863686378638863986408641864286438644864586468647864886498650865186528653865486558656865786588659866086618662866386648665866686678668866986708671867286738674867586768677867886798680868186828683868486858686868786888689869086918692869386948695869686978698869987008701870287038704870587068707870887098710871187128713871487158716871787188719872087218722872387248725872687278728872987308731873287338734873587368737873887398740874187428743874487458746874787488749875087518752875387548755875687578758875987608761876287638764876587668767876887698770877187728773877487758776877787788779878087818782878387848785878687878788878987908791879287938794879587968797879887998800880188028803880488058806880788088809881088118812881388148815881688178818881988208821882288238824882588268827882888298830883188328833883488358836883788388839884088418842884388448845884688478848884988508851885288538854885588568857885888598860886188628863886488658866886788688869887088718872887388748875887688778878887988808881888288838884888588868887888888898890889188928893889488958896889788988899890089018902890389048905890689078908890989108911891289138914891589168917891889198920892189228923892489258926892789288929893089318932893389348935893689378938893989408941894289438944894589468947894889498950895189528953895489558956895789588959896089618962896389648965896689678968896989708971897289738974897589768977897889798980898189828983898489858986898789888989899089918992899389948995899689978998899990009001900290039004900590069007900890099010901190129013901490159016901790189019902090219022902390249025902690279028902990309031903290339034903590369037903890399040904190429043904490459046904790489049905090519052905390549055905690579058905990609061906290639064906590669067906890699070907190729073907490759076907790789079908090819082908390849085908690879088908990909091909290939094909590969097909890999100910191029103910491059106910791089109911091119112911391149115911691179118911991209121912291239124912591269127912891299130913191329133913491359136913791389139914091419142914391449145914691479148914991509151915291539154915591569157915891599160916191629163916491659166916791689169917091719172917391749175917691779178917991809181918291839184918591869187918891899190919191929193919491959196919791989199920092019202920392049205920692079208920992109211921292139214921592169217921892199220922192229223922492259226922792289229923092319232923392349235923692379238923992409241924292439244924592469247924892499250925192529253925492559256925792589259926092619262926392649265926692679268926992709271927292739274927592769277927892799280928192829283928492859286928792889289929092919292929392949295929692979298929993009301930293039304930593069307930893099310931193129313931493159316931793189319932093219322932393249325932693279328932993309331933293339334933593369337933893399340934193429343934493459346934793489349935093519352935393549355935693579358935993609361936293639364936593669367936893699370937193729373937493759376937793789379938093819382938393849385938693879388938993909391939293939394939593969397939893999400940194029403940494059406940794089409941094119412941394149415941694179418941994209421942294239424942594269427942894299430943194329433943494359436943794389439944094419442944394449445944694479448944994509451945294539454945594569457945894599460946194629463946494659466946794689469947094719472947394749475947694779478947994809481948294839484948594869487948894899490949194929493949494959496949794989499950095019502950395049505950695079508950995109511951295139514951595169517951895199520952195229523952495259526952795289529953095319532953395349535953695379538953995409541954295439544954595469547954895499550955195529553955495559556955795589559956095619562956395649565956695679568956995709571957295739574957595769577957895799580958195829583958495859586958795889589959095919592959395949595959695979598959996009601960296039604960596069607960896099610961196129613961496159616961796189619962096219622962396249625962696279628962996309631963296339634963596369637963896399640964196429643964496459646964796489649965096519652965396549655965696579658965996609661966296639664966596669667966896699670967196729673967496759676967796789679968096819682968396849685968696879688968996909691969296939694969596969697969896999700970197029703970497059706970797089709971097119712971397149715971697179718971997209721972297239724972597269727972897299730973197329733973497359736973797389739974097419742974397449745974697479748974997509751975297539754975597569757975897599760976197629763976497659766976797689769977097719772977397749775977697779778977997809781978297839784978597869787978897899790979197929793979497959796979797989799980098019802980398049805980698079808980998109811981298139814981598169817981898199820982198229823982498259826982798289829983098319832983398349835983698379838983998409841984298439844984598469847984898499850985198529853985498559856985798589859986098619862986398649865986698679868986998709871987298739874987598769877987898799880988198829883988498859886988798889889989098919892989398949895989698979898989999009901990299039904990599069907990899099910991199129913991499159916991799189919992099219922992399249925992699279928992999309931993299339934993599369937993899399940994199429943994499459946994799489949995099519952995399549955995699579958995999609961996299639964996599669967996899699970997199729973997499759976997799789979998099819982998399849985998699879988998999909991999299939994999599969997999899991000010001100021000310004100051000610007100081000910010100111001210013100141001510016100171001810019100201002110022100231002410025100261002710028100291003010031100321003310034100351003610037100381003910040100411004210043100441004510046100471004810049100501005110052100531005410055100561005710058100591006010061100621006310064100651006610067100681006910070100711007210073100741007510076100771007810079100801008110082100831008410085100861008710088100891009010091100921009310094100951009610097100981009910100101011010210103101041010510106101071010810109101101011110112101131011410115101161011710118101191012010121101221012310124101251012610127101281012910130101311013210133101341013510136101371013810139101401014110142101431014410145101461014710148101491015010151101521015310154101551015610157101581015910160101611016210163101641016510166101671016810169101701017110172101731017410175101761017710178101791018010181101821018310184101851018610187101881018910190101911019210193101941019510196101971019810199102001020110202102031020410205102061020710208102091021010211102121021310214102151021610217102181021910220102211022210223102241022510226102271022810229102301023110232102331023410235102361023710238102391024010241102421024310244102451024610247102481024910250102511025210253102541025510256102571025810259102601026110262102631026410265102661026710268102691027010271102721027310274102751027610277102781027910280102811028210283102841028510286102871028810289102901029110292102931029410295102961029710298102991030010301103021030310304103051030610307103081030910310103111031210313103141031510316103171031810319103201032110322103231032410325103261032710328103291033010331103321033310334103351033610337103381033910340103411034210343103441034510346103471034810349103501035110352103531035410355103561035710358103591036010361103621036310364103651036610367103681036910370103711037210373103741037510376103771037810379103801038110382103831038410385103861038710388103891039010391103921039310394103951039610397103981039910400104011040210403104041040510406104071040810409104101041110412104131041410415104161041710418104191042010421104221042310424104251042610427104281042910430104311043210433104341043510436104371043810439104401044110442104431044410445104461044710448104491045010451104521045310454104551045610457104581045910460104611046210463104641046510466104671046810469104701047110472104731047410475104761047710478104791048010481104821048310484104851048610487104881048910490104911049210493104941049510496104971049810499105001050110502105031050410505105061050710508105091051010511105121051310514105151051610517105181051910520105211052210523105241052510526105271052810529105301053110532105331053410535105361053710538105391054010541105421054310544105451054610547105481054910550105511055210553105541055510556105571055810559105601056110562105631056410565105661056710568105691057010571105721057310574105751057610577105781057910580105811058210583105841058510586105871058810589105901059110592105931059410595105961059710598105991060010601106021060310604106051060610607106081060910610106111061210613106141061510616106171061810619106201062110622106231062410625106261062710628106291063010631106321063310634106351063610637106381063910640106411064210643106441064510646106471064810649106501065110652106531065410655106561065710658106591066010661106621066310664106651066610667106681066910670106711067210673106741067510676106771067810679106801068110682106831068410685106861068710688106891069010691106921069310694106951069610697106981069910700107011070210703107041070510706107071070810709107101071110712107131071410715107161071710718107191072010721107221072310724107251072610727107281072910730107311073210733107341073510736107371073810739107401074110742107431074410745107461074710748107491075010751107521075310754107551075610757107581075910760107611076210763107641076510766107671076810769107701077110772107731077410775107761077710778107791078010781107821078310784107851078610787107881078910790107911079210793107941079510796107971079810799108001080110802108031080410805108061080710808108091081010811108121081310814108151081610817108181081910820108211082210823108241082510826108271082810829108301083110832108331083410835108361083710838108391084010841108421084310844108451084610847108481084910850108511085210853108541085510856108571085810859108601086110862108631086410865108661086710868108691087010871108721087310874108751087610877108781087910880108811088210883108841088510886108871088810889108901089110892108931089410895108961089710898108991090010901109021090310904109051090610907109081090910910109111091210913109141091510916109171091810919109201092110922109231092410925109261092710928109291093010931109321093310934109351093610937109381093910940109411094210943109441094510946109471094810949109501095110952109531095410955109561095710958109591096010961109621096310964109651096610967109681096910970109711097210973109741097510976109771097810979109801098110982109831098410985109861098710988109891099010991109921099310994109951099610997109981099911000110011100211003110041100511006110071100811009110101101111012110131101411015110161101711018110191102011021110221102311024110251102611027110281102911030110311103211033110341103511036110371103811039110401104111042110431104411045110461104711048110491105011051110521105311054110551105611057110581105911060110611106211063110641106511066110671106811069110701107111072110731107411075110761107711078110791108011081110821108311084110851108611087110881108911090110911109211093110941109511096110971109811099111001110111102111031110411105111061110711108111091111011111111121111311114111151111611117111181111911120111211112211123111241112511126111271112811129111301113111132111331113411135111361113711138111391114011141111421114311144111451114611147111481114911150111511115211153111541115511156111571115811159111601116111162111631116411165111661116711168111691117011171111721117311174111751117611177111781117911180111811118211183111841118511186111871118811189111901119111192111931119411195111961119711198111991120011201112021120311204112051120611207112081120911210112111121211213112141121511216112171121811219112201122111222112231122411225112261122711228112291123011231112321123311234112351123611237112381123911240112411124211243112441124511246112471124811249112501125111252112531125411255112561125711258112591126011261112621126311264112651126611267112681126911270112711127211273112741127511276112771127811279112801128111282112831128411285112861128711288112891129011291112921129311294112951129611297112981129911300113011130211303113041130511306113071130811309113101131111312113131131411315113161131711318113191132011321113221132311324113251132611327113281132911330113311133211333113341133511336113371133811339113401134111342113431134411345113461134711348113491135011351113521135311354113551135611357113581135911360113611136211363113641136511366113671136811369113701137111372113731137411375113761137711378113791138011381113821138311384113851138611387113881138911390113911139211393113941139511396113971139811399114001140111402114031140411405114061140711408114091141011411114121141311414114151141611417114181141911420114211142211423114241142511426114271142811429114301143111432114331143411435114361143711438114391144011441114421144311444114451144611447114481144911450114511145211453114541145511456114571145811459114601146111462114631146411465114661146711468114691147011471114721147311474114751147611477114781147911480114811148211483114841148511486114871148811489114901149111492114931149411495114961149711498114991150011501115021150311504115051150611507115081150911510115111151211513115141151511516115171151811519115201152111522115231152411525115261152711528115291153011531115321153311534115351153611537115381153911540115411154211543115441154511546115471154811549115501155111552115531155411555115561155711558115591156011561115621156311564115651156611567115681156911570115711157211573115741157511576115771157811579115801158111582115831158411585115861158711588115891159011591115921159311594115951159611597115981159911600116011160211603116041160511606116071160811609116101161111612116131161411615116161161711618116191162011621116221162311624116251162611627116281162911630116311163211633116341163511636116371163811639116401164111642116431164411645116461164711648116491165011651116521165311654116551165611657116581165911660116611166211663116641166511666116671166811669116701167111672116731167411675116761167711678116791168011681116821168311684116851168611687116881168911690116911169211693116941169511696116971169811699117001170111702117031170411705117061170711708117091171011711117121171311714117151171611717117181171911720117211172211723117241172511726117271172811729117301173111732117331173411735117361173711738117391174011741117421174311744117451174611747117481174911750117511175211753117541175511756117571175811759117601176111762117631176411765117661176711768117691177011771117721177311774117751177611777117781177911780117811178211783117841178511786117871178811789117901179111792117931179411795117961179711798117991180011801118021180311804118051180611807118081180911810118111181211813118141181511816118171181811819118201182111822118231182411825118261182711828118291183011831118321183311834118351183611837118381183911840118411184211843118441184511846118471184811849118501185111852118531185411855118561185711858118591186011861118621186311864118651186611867118681186911870118711187211873118741187511876118771187811879118801188111882118831188411885118861188711888118891189011891118921189311894118951189611897118981189911900119011190211903119041190511906119071190811909119101191111912119131191411915119161191711918119191192011921119221192311924119251192611927119281192911930119311193211933119341193511936119371193811939119401194111942119431194411945119461194711948119491195011951119521195311954119551195611957119581195911960119611196211963119641196511966119671196811969119701197111972119731197411975119761197711978119791198011981119821198311984119851198611987119881198911990119911199211993119941199511996119971199811999120001200112002120031200412005120061200712008120091201012011120121201312014120151201612017120181201912020120211202212023120241202512026120271202812029120301203112032120331203412035120361203712038120391204012041120421204312044120451204612047120481204912050120511205212053120541205512056120571205812059120601206112062120631206412065120661206712068120691207012071120721207312074120751207612077120781207912080120811208212083120841208512086120871208812089120901209112092120931209412095120961209712098120991210012101121021210312104121051210612107121081210912110121111211212113121141211512116121171211812119121201212112122121231212412125121261212712128121291213012131121321213312134121351213612137121381213912140121411214212143121441214512146121471214812149121501215112152121531215412155121561215712158121591216012161121621216312164121651216612167121681216912170121711217212173121741217512176121771217812179121801218112182121831218412185121861218712188121891219012191121921219312194121951219612197121981219912200122011220212203122041220512206122071220812209122101221112212122131221412215122161221712218122191222012221122221222312224122251222612227122281222912230122311223212233122341223512236122371223812239122401224112242122431224412245122461224712248122491225012251122521225312254122551225612257122581225912260122611226212263122641226512266122671226812269122701227112272122731227412275122761227712278122791228012281122821228312284122851228612287122881228912290122911229212293122941229512296122971229812299123001230112302123031230412305123061230712308123091231012311123121231312314123151231612317123181231912320123211232212323123241232512326123271232812329123301233112332123331233412335123361233712338123391234012341123421234312344123451234612347123481234912350123511235212353123541235512356123571235812359123601236112362123631236412365123661236712368123691237012371123721237312374123751237612377123781237912380123811238212383123841238512386123871238812389123901239112392123931239412395123961239712398123991240012401124021240312404124051240612407124081240912410124111241212413124141241512416124171241812419124201242112422124231242412425124261242712428124291243012431124321243312434124351243612437124381243912440124411244212443124441244512446124471244812449124501245112452124531245412455124561245712458124591246012461124621246312464124651246612467124681246912470124711247212473124741247512476124771247812479124801248112482124831248412485124861248712488124891249012491124921249312494124951249612497124981249912500125011250212503125041250512506125071250812509125101251112512125131251412515125161251712518125191252012521125221252312524125251252612527125281252912530125311253212533125341253512536125371253812539125401254112542125431254412545125461254712548125491255012551125521255312554125551255612557125581255912560125611256212563125641256512566125671256812569125701257112572125731257412575125761257712578125791258012581125821258312584125851258612587125881258912590125911259212593125941259512596125971259812599126001260112602126031260412605126061260712608126091261012611126121261312614126151261612617126181261912620126211262212623126241262512626126271262812629126301263112632126331263412635126361263712638126391264012641126421264312644126451264612647126481264912650126511265212653126541265512656126571265812659126601266112662126631266412665126661266712668126691267012671126721267312674126751267612677126781267912680126811268212683126841268512686126871268812689126901269112692126931269412695126961269712698126991270012701127021270312704127051270612707127081270912710127111271212713127141271512716127171271812719127201272112722127231272412725127261272712728127291273012731127321273312734127351273612737127381273912740127411274212743127441274512746127471274812749127501275112752127531275412755127561275712758127591276012761127621276312764127651276612767127681276912770127711277212773127741277512776127771277812779127801278112782127831278412785127861278712788127891279012791127921279312794127951279612797127981279912800128011280212803128041280512806128071280812809128101281112812128131281412815128161281712818128191282012821128221282312824128251282612827128281282912830128311283212833128341283512836128371283812839128401284112842128431284412845128461284712848128491285012851128521285312854128551285612857128581285912860128611286212863128641286512866128671286812869128701287112872128731287412875128761287712878128791288012881128821288312884128851288612887128881288912890128911289212893128941289512896128971289812899129001290112902129031290412905129061290712908129091291012911129121291312914129151291612917129181291912920129211292212923129241292512926129271292812929129301293112932129331293412935129361293712938129391294012941129421294312944129451294612947129481294912950129511295212953129541295512956129571295812959129601296112962129631296412965129661296712968129691297012971129721297312974129751297612977129781297912980129811298212983129841298512986129871298812989129901299112992129931299412995129961299712998129991300013001130021300313004130051300613007130081300913010130111301213013130141301513016130171301813019130201302113022130231302413025130261302713028130291303013031130321303313034130351303613037130381303913040130411304213043130441304513046130471304813049130501305113052130531305413055130561305713058130591306013061130621306313064130651306613067130681306913070130711307213073130741307513076130771307813079130801308113082130831308413085130861308713088130891309013091130921309313094130951309613097130981309913100131011310213103131041310513106131071310813109131101311113112131131311413115131161311713118131191312013121131221312313124131251312613127131281312913130131311313213133131341313513136131371313813139131401314113142131431314413145131461314713148131491315013151131521315313154131551315613157131581315913160131611316213163131641316513166131671316813169131701317113172131731317413175131761317713178131791318013181131821318313184131851318613187131881318913190131911319213193131941319513196131971319813199132001320113202132031320413205132061320713208132091321013211132121321313214132151321613217132181321913220132211322213223132241322513226132271322813229132301323113232132331323413235132361323713238132391324013241132421324313244132451324613247132481324913250132511325213253132541325513256132571325813259132601326113262132631326413265132661326713268132691327013271132721327313274132751327613277132781327913280132811328213283132841328513286132871328813289132901329113292132931329413295132961329713298132991330013301133021330313304133051330613307133081330913310133111331213313133141331513316133171331813319133201332113322133231332413325133261332713328133291333013331133321333313334133351333613337133381333913340133411334213343133441334513346133471334813349133501335113352133531335413355133561335713358133591336013361133621336313364133651336613367133681336913370133711337213373133741337513376133771337813379133801338113382133831338413385133861338713388133891339013391133921339313394133951339613397133981339913400134011340213403134041340513406134071340813409134101341113412134131341413415134161341713418134191342013421134221342313424134251342613427134281342913430134311343213433134341343513436134371343813439134401344113442134431344413445134461344713448134491345013451134521345313454134551345613457134581345913460134611346213463134641346513466134671346813469134701347113472134731347413475134761347713478134791348013481134821348313484134851348613487134881348913490134911349213493134941349513496134971349813499135001350113502135031350413505135061350713508135091351013511135121351313514135151351613517135181351913520135211352213523135241352513526135271352813529135301353113532135331353413535135361353713538135391354013541135421354313544135451354613547135481354913550135511355213553135541355513556135571355813559135601356113562135631356413565135661356713568135691357013571135721357313574135751357613577135781357913580135811358213583135841358513586135871358813589135901359113592135931359413595135961359713598135991360013601136021360313604136051360613607136081360913610136111361213613136141361513616136171361813619136201362113622136231362413625136261362713628136291363013631136321363313634136351363613637136381363913640136411364213643136441364513646136471364813649136501365113652136531365413655136561365713658136591366013661136621366313664136651366613667136681366913670136711367213673136741367513676136771367813679136801368113682136831368413685136861368713688136891369013691136921369313694136951369613697136981369913700137011370213703137041370513706137071370813709137101371113712137131371413715137161371713718137191372013721137221372313724137251372613727137281372913730137311373213733137341373513736137371373813739137401374113742137431374413745137461374713748137491375013751137521375313754137551375613757137581375913760137611376213763137641376513766137671376813769137701377113772137731377413775137761377713778137791378013781137821378313784137851378613787137881378913790137911379213793137941379513796137971379813799138001380113802138031380413805138061380713808138091381013811138121381313814138151381613817138181381913820138211382213823138241382513826138271382813829138301383113832138331383413835138361383713838138391384013841138421384313844138451384613847138481384913850138511385213853138541385513856138571385813859138601386113862138631386413865138661386713868138691387013871138721387313874138751387613877138781387913880138811388213883138841388513886138871388813889138901389113892138931389413895138961389713898138991390013901139021390313904139051390613907139081390913910139111391213913139141391513916139171391813919139201392113922139231392413925139261392713928139291393013931139321393313934139351393613937139381393913940139411394213943139441394513946139471394813949139501395113952139531395413955139561395713958139591396013961139621396313964139651396613967139681396913970139711397213973139741397513976139771397813979139801398113982139831398413985139861398713988139891399013991139921399313994139951399613997139981399914000140011400214003140041400514006140071400814009140101401114012140131401414015140161401714018140191402014021140221402314024
  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_GLCORE
  18. #define SOKOL_GLES3
  19. #define SOKOL_D3D11
  20. #define SOKOL_METAL
  21. #define SOKOL_WGPU
  22. #define SOKOL_NOAPI
  23. Optionally provide the following defines with your own implementations:
  24. SOKOL_ASSERT(c) - your own assert macro (default: assert(c))
  25. SOKOL_UNREACHABLE() - a guard macro for unreachable code (default: assert(false))
  26. SOKOL_WIN32_FORCE_MAIN - define this on Win32 to add a main() entry point
  27. SOKOL_WIN32_FORCE_WINMAIN - define this on Win32 to add a WinMain() entry point (enabled by default unless
  28. SOKOL_WIN32_FORCE_MAIN or SOKOL_NO_ENTRY is defined)
  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. Optionally define the following to force debug checks and validations
  34. even in release mode:
  35. SOKOL_DEBUG - by default this is defined if NDEBUG is not defined
  36. If sokol_app.h is compiled as a DLL, define the following before
  37. including the declaration or implementation:
  38. SOKOL_DLL
  39. On Windows, SOKOL_DLL will define SOKOL_APP_API_DECL as __declspec(dllexport)
  40. or __declspec(dllimport) as needed.
  41. if SOKOL_WIN32_FORCE_MAIN and SOKOL_WIN32_FORCE_WINMAIN are both defined,
  42. it is up to the developer to define the desired subsystem.
  43. On Linux, SOKOL_GLCORE can use either GLX or EGL.
  44. GLX is default, set SOKOL_FORCE_EGL to override.
  45. For example code, see https://github.com/floooh/sokol-samples/tree/master/sapp
  46. Portions of the Windows and Linux GL initialization, event-, icon- etc... code
  47. have been taken from GLFW (http://www.glfw.org/).
  48. iOS onscreen keyboard support 'inspired' by libgdx.
  49. Link with the following system libraries:
  50. - on macOS:
  51. - all backends: Foundation, Cocoa, QuartzCore
  52. - with SOKOL_METAL: Metal, MetalKit
  53. - with SOKOL_GLCORE: OpenGL
  54. - with SOKOL_WGPU: a WebGPU implementation library (tested with webgpu_dawn)
  55. - on iOS:
  56. - all backends: Foundation, UIKit
  57. - with SOKOL_METAL: Metal, MetalKit
  58. - with SOKOL_GLES3: OpenGLES, GLKit
  59. - on Linux:
  60. - all backends: X11, Xi, Xcursor, dl, pthread, m
  61. - with SOKOL_GLCORE: GL
  62. - with SOKOL_GLES3: GLESv2
  63. - with SOKOL_WGPU: a WebGPU implementation library (tested with webgpu_dawn)
  64. - with EGL: EGL
  65. - on Android: GLESv3, EGL, log, android
  66. - on Windows:
  67. - with MSVC or Clang: library dependencies are defined via `#pragma comment`
  68. - with SOKOL_WGPU: a WebGPU implementation library (tested with webgpu_dawn)
  69. - with MINGW/MSYS2 gcc:
  70. - compile with '-mwin32' so that _WIN32 is defined
  71. - link with the following libs: -lkernel32 -luser32 -lshell32
  72. - additionally with the GL backend: -lgdi32
  73. - additionally with the D3D11 backend: -ld3d11 -ldxgi
  74. On Linux, you also need to use the -pthread compiler and linker option, otherwise weird
  75. things will happen, see here for details: https://github.com/floooh/sokol/issues/376
  76. On macOS and iOS, the implementation must be compiled as Objective-C.
  77. On Emscripten:
  78. - for WebGL2: add the linker option `-s USE_WEBGL2=1`
  79. - for WebGPU: compile and link with `--use-port=emdawnwebgpu`
  80. (for more exotic situations read: https://dawn.googlesource.com/dawn/+/refs/heads/main/src/emdawnwebgpu/pkg/README.md)
  81. FEATURE OVERVIEW
  82. ================
  83. sokol_app.h provides a minimalistic cross-platform API which
  84. implements the 'application-wrapper' parts of a 3D application:
  85. - a common application entry function
  86. - creates a window and 3D-API context/device with a swapchain
  87. surface, depth-stencil-buffer surface and optionally MSAA surface
  88. - makes the rendered frame visible
  89. - provides keyboard-, mouse- and low-level touch-events
  90. - platforms: MacOS, iOS, HTML5, Win32, Linux/RaspberryPi, Android
  91. - 3D-APIs: Metal, D3D11, GL4.1, GL4.3, GLES3, WebGL2, WebGPU, NOAPI
  92. FEATURE/PLATFORM MATRIX
  93. =======================
  94. | Windows | macOS | Linux | iOS | Android | HTML5
  95. --------------------+---------+-------+-------+-------+---------+--------
  96. gl 4.x | YES | YES | YES | --- | --- | ---
  97. gles3/webgl2 | --- | --- | YES(2)| YES | YES | YES
  98. metal | --- | YES | --- | YES | --- | ---
  99. d3d11 | YES | --- | --- | --- | --- | ---
  100. webgpu | YES(4) | YES(4)| YES(4)| NO | NO | YES
  101. noapi | YES | TODO | TODO | --- | TODO | ---
  102. KEY_DOWN | YES | YES | YES | SOME | TODO | YES
  103. KEY_UP | YES | YES | YES | SOME | TODO | YES
  104. CHAR | YES | YES | YES | YES | TODO | YES
  105. MOUSE_DOWN | YES | YES | YES | --- | --- | YES
  106. MOUSE_UP | YES | YES | YES | --- | --- | YES
  107. MOUSE_SCROLL | YES | YES | YES | --- | --- | YES
  108. MOUSE_MOVE | YES | YES | YES | --- | --- | YES
  109. MOUSE_ENTER | YES | YES | YES | --- | --- | YES
  110. MOUSE_LEAVE | YES | YES | YES | --- | --- | YES
  111. TOUCHES_BEGAN | --- | --- | --- | YES | YES | YES
  112. TOUCHES_MOVED | --- | --- | --- | YES | YES | YES
  113. TOUCHES_ENDED | --- | --- | --- | YES | YES | YES
  114. TOUCHES_CANCELLED | --- | --- | --- | YES | YES | YES
  115. RESIZED | YES | YES | YES | YES | YES | YES
  116. ICONIFIED | YES | YES | YES | --- | --- | ---
  117. RESTORED | YES | YES | YES | --- | --- | ---
  118. FOCUSED | YES | YES | YES | --- | --- | YES
  119. UNFOCUSED | YES | YES | YES | --- | --- | YES
  120. SUSPENDED | --- | --- | --- | YES | YES | TODO
  121. RESUMED | --- | --- | --- | YES | YES | TODO
  122. QUIT_REQUESTED | YES | YES | YES | --- | --- | YES
  123. IME | TODO | TODO? | TODO | ??? | TODO | ???
  124. key repeat flag | YES | YES | YES | --- | --- | YES
  125. windowed | YES | YES | YES | --- | --- | YES
  126. fullscreen | YES | YES | YES | YES | YES | YES(3)
  127. mouse hide | YES | YES | YES | --- | --- | YES
  128. mouse lock | YES | YES | YES | --- | --- | YES
  129. set cursor type | YES | YES | YES | --- | --- | YES
  130. screen keyboard | --- | --- | --- | YES | TODO | YES
  131. swap interval | YES | YES | YES | YES | TODO | YES
  132. high-dpi | YES | YES | TODO | YES | YES | YES
  133. clipboard | YES | YES | YES | --- | --- | YES
  134. MSAA | YES | YES | YES | YES | YES | YES
  135. drag'n'drop | YES | YES | YES | --- | --- | YES
  136. window icon | YES | YES(1)| YES | --- | --- | YES
  137. (1) macOS has no regular window icons, instead the dock icon is changed
  138. (2) supported with EGL only (not GLX)
  139. (3) fullscreen in the browser not supported on iphones
  140. (4) WebGPU on native desktop platforms should be considered experimental
  141. and mainly useful for debugging and benchmarking
  142. STEP BY STEP
  143. ============
  144. --- Add a sokol_main() function to your code which returns a sapp_desc structure
  145. with initialization parameters and callback function pointers. This
  146. function is called very early, usually at the start of the
  147. platform's entry function (e.g. main or WinMain). You should do as
  148. little as possible here, since the rest of your code might be called
  149. from another thread (this depends on the platform):
  150. sapp_desc sokol_main(int argc, char* argv[]) {
  151. return (sapp_desc) {
  152. .width = 640,
  153. .height = 480,
  154. .init_cb = my_init_func,
  155. .frame_cb = my_frame_func,
  156. .cleanup_cb = my_cleanup_func,
  157. .event_cb = my_event_func,
  158. ...
  159. };
  160. }
  161. To get any logging output in case of errors you need to provide a log
  162. callback. The easiest way is via sokol_log.h:
  163. #include "sokol_log.h"
  164. sapp_desc sokol_main(int argc, char* argv[]) {
  165. return (sapp_desc) {
  166. ...
  167. .logger.func = slog_func,
  168. };
  169. }
  170. There are many more setup parameters, but these are the most important.
  171. For a complete list search for the sapp_desc structure declaration
  172. below.
  173. DO NOT call any sokol-app function from inside sokol_main(), since
  174. sokol-app will not be initialized at this point.
  175. The .width and .height parameters are the preferred size of the 3D
  176. rendering canvas. The actual size may differ from this depending on
  177. platform and other circumstances. Also the canvas size may change at
  178. any time (for instance when the user resizes the application window,
  179. or rotates the mobile device). You can just keep .width and .height
  180. zero-initialized to open a default-sized window (what "default-size"
  181. exactly means is platform-specific, but usually it's a size that covers
  182. most of, but not all, of the display).
  183. All provided function callbacks will be called from the same thread,
  184. but this may be different from the thread where sokol_main() was called.
  185. .init_cb (void (*)(void))
  186. This function is called once after the application window,
  187. 3D rendering context and swap chain have been created. The
  188. function takes no arguments and has no return value.
  189. .frame_cb (void (*)(void))
  190. This is the per-frame callback, which is usually called 60
  191. times per second. This is where your application would update
  192. most of its state and perform all rendering.
  193. .cleanup_cb (void (*)(void))
  194. The cleanup callback is called once right before the application
  195. quits.
  196. .event_cb (void (*)(const sapp_event* event))
  197. The event callback is mainly for input handling, but is also
  198. used to communicate other types of events to the application. Keep the
  199. event_cb struct member zero-initialized if your application doesn't require
  200. event handling.
  201. As you can see, those 'standard callbacks' don't have a user_data
  202. argument, so any data that needs to be preserved between callbacks
  203. must live in global variables. If keeping state in global variables
  204. is not an option, there's an alternative set of callbacks with
  205. an additional user_data pointer argument:
  206. .user_data (void*)
  207. The user-data argument for the callbacks below
  208. .init_userdata_cb (void (*)(void* user_data))
  209. .frame_userdata_cb (void (*)(void* user_data))
  210. .cleanup_userdata_cb (void (*)(void* user_data))
  211. .event_userdata_cb (void(*)(const sapp_event* event, void* user_data))
  212. The function sapp_userdata() can be used to query the user_data
  213. pointer provided in the sapp_desc struct.
  214. You can also call sapp_query_desc() to get a copy of the
  215. original sapp_desc structure.
  216. NOTE that there's also an alternative compile mode where sokol_app.h
  217. doesn't "hijack" the main() function. Search below for SOKOL_NO_ENTRY.
  218. --- Implement the initialization callback function (init_cb), this is called
  219. once after the rendering surface, 3D API and swap chain have been
  220. initialized by sokol_app. All sokol-app functions can be called
  221. from inside the initialization callback, the most useful functions
  222. at this point are:
  223. int sapp_width(void)
  224. int sapp_height(void)
  225. Returns the current width and height of the default framebuffer in pixels,
  226. this may change from one frame to the next, and it may be different
  227. from the initial size provided in the sapp_desc struct.
  228. float sapp_widthf(void)
  229. float sapp_heightf(void)
  230. These are alternatives to sapp_width() and sapp_height() which return
  231. the default framebuffer size as float values instead of integer. This
  232. may help to prevent casting back and forth between int and float
  233. in more strongly typed languages than C and C++.
  234. double sapp_frame_duration(void)
  235. Returns the frame duration in seconds averaged over a number of
  236. frames to smooth out any jittering spikes.
  237. int sapp_color_format(void)
  238. int sapp_depth_format(void)
  239. The color and depth-stencil pixelformats of the default framebuffer,
  240. as integer values which are compatible with sokol-gfx's
  241. sg_pixel_format enum (so that they can be plugged directly in places
  242. where sg_pixel_format is expected). Possible values are:
  243. 23 == SG_PIXELFORMAT_RGBA8
  244. 28 == SG_PIXELFORMAT_BGRA8
  245. 42 == SG_PIXELFORMAT_DEPTH
  246. 43 == SG_PIXELFORMAT_DEPTH_STENCIL
  247. int sapp_sample_count(void)
  248. Return the MSAA sample count of the default framebuffer.
  249. const void* sapp_metal_get_device(void)
  250. const void* sapp_metal_get_current_drawable(void)
  251. const void* sapp_metal_get_depth_stencil_texture(void)
  252. const void* sapp_metal_get_msaa_color_texture(void)
  253. If the Metal backend has been selected, these functions return pointers
  254. to various Metal API objects required for rendering, otherwise
  255. they return a null pointer. These void pointers are actually
  256. Objective-C ids converted with a (ARC) __bridge cast so that
  257. the ids can be tunneled through C code. Also note that the returned
  258. pointers may change from one frame to the next, only the Metal device
  259. object is guaranteed to stay the same.
  260. const void* sapp_macos_get_window(void)
  261. On macOS, get the NSWindow object pointer, otherwise a null pointer.
  262. Before being used as Objective-C object, the void* must be converted
  263. back with a (ARC) __bridge cast.
  264. const void* sapp_ios_get_window(void)
  265. On iOS, get the UIWindow object pointer, otherwise a null pointer.
  266. Before being used as Objective-C object, the void* must be converted
  267. back with a (ARC) __bridge cast.
  268. const void* sapp_d3d11_get_device(void)
  269. const void* sapp_d3d11_get_device_context(void)
  270. const void* sapp_d3d11_get_render_view(void)
  271. const void* sapp_d3d11_get_resolve_view(void);
  272. const void* sapp_d3d11_get_depth_stencil_view(void)
  273. Similar to the sapp_metal_* functions, the sapp_d3d11_* functions
  274. return pointers to D3D11 API objects required for rendering,
  275. only if the D3D11 backend has been selected. Otherwise they
  276. return a null pointer. Note that the returned pointers to the
  277. render-target-view and depth-stencil-view may change from one
  278. frame to the next!
  279. const void* sapp_win32_get_hwnd(void)
  280. On Windows, get the window's HWND, otherwise a null pointer. The
  281. HWND has been cast to a void pointer in order to be tunneled
  282. through code which doesn't include Windows.h.
  283. const void* sapp_x11_get_window(void)
  284. On Linux, get the X11 Window, otherwise a null pointer. The
  285. Window has been cast to a void pointer in order to be tunneled
  286. through code which doesn't include X11/Xlib.h.
  287. const void* sapp_x11_get_display(void)
  288. On Linux, get the X11 Display, otherwise a null pointer. The
  289. Display has been cast to a void pointer in order to be tunneled
  290. through code which doesn't include X11/Xlib.h.
  291. const void* sapp_wgpu_get_device(void)
  292. const void* sapp_wgpu_get_render_view(void)
  293. const void* sapp_wgpu_get_resolve_view(void)
  294. const void* sapp_wgpu_get_depth_stencil_view(void)
  295. These are the WebGPU-specific functions to get the WebGPU
  296. objects and values required for rendering. If sokol_app.h
  297. is not compiled with SOKOL_WGPU, these functions return null.
  298. uint32_t sapp_gl_get_framebuffer(void)
  299. This returns the 'default framebuffer' of the GL context.
  300. Typically this will be zero.
  301. int sapp_gl_get_major_version(void)
  302. int sapp_gl_get_minor_version(void)
  303. bool sapp_gl_is_gles(void)
  304. Returns the major and minor version of the GL context and
  305. whether the GL context is a GLES context
  306. const void* sapp_android_get_native_activity(void);
  307. On Android, get the native activity ANativeActivity pointer, otherwise
  308. a null pointer.
  309. --- Implement the frame-callback function, this function will be called
  310. on the same thread as the init callback, but might be on a different
  311. thread than the sokol_main() function. Note that the size of
  312. the rendering framebuffer might have changed since the frame callback
  313. was called last. Call the functions sapp_width() and sapp_height()
  314. each frame to get the current size.
  315. --- Optionally implement the event-callback to handle input events.
  316. sokol-app provides the following type of input events:
  317. - a 'virtual key' was pressed down or released
  318. - a single text character was entered (provided as UTF-32 encoded
  319. UNICODE code point)
  320. - a mouse button was pressed down or released (left, right, middle)
  321. - mouse-wheel or 2D scrolling events
  322. - the mouse was moved
  323. - the mouse has entered or left the application window boundaries
  324. - low-level, portable multi-touch events (began, moved, ended, cancelled)
  325. - the application window was resized, iconified or restored
  326. - the application was suspended or restored (on mobile platforms)
  327. - the user or application code has asked to quit the application
  328. - a string was pasted to the system clipboard
  329. - one or more files have been dropped onto the application window
  330. To explicitly 'consume' an event and prevent that the event is
  331. forwarded for further handling to the operating system, call
  332. sapp_consume_event() from inside the event handler (NOTE that
  333. this behaviour is currently only implemented for some HTML5
  334. events, support for other platforms and event types will
  335. be added as needed, please open a GitHub ticket and/or provide
  336. a PR if needed).
  337. NOTE: Do *not* call any 3D API rendering functions in the event
  338. callback function, since the 3D API context may not be active when the
  339. event callback is called (it may work on some platforms and 3D APIs,
  340. but not others, and the exact behaviour may change between
  341. sokol-app versions).
  342. --- Implement the cleanup-callback function, this is called once
  343. after the user quits the application (see the section
  344. "APPLICATION QUIT" for detailed information on quitting
  345. behaviour, and how to intercept a pending quit - for instance to show a
  346. "Really Quit?" dialog box). Note that the cleanup-callback isn't
  347. guaranteed to be called on the web and mobile platforms.
  348. MOUSE CURSOR TYPE AND VISIBILITY
  349. ================================
  350. You can show and hide the mouse cursor with
  351. void sapp_show_mouse(bool show)
  352. And to get the current shown status:
  353. bool sapp_mouse_shown(void)
  354. NOTE that hiding the mouse cursor is different and independent from
  355. the MOUSE/POINTER LOCK feature which will also hide the mouse pointer when
  356. active (MOUSE LOCK is described below).
  357. To change the mouse cursor to one of several predefined types, call
  358. the function:
  359. void sapp_set_mouse_cursor(sapp_mouse_cursor cursor)
  360. Setting the default mouse cursor SAPP_MOUSECURSOR_DEFAULT will restore
  361. the standard look.
  362. To get the currently active mouse cursor type, call:
  363. sapp_mouse_cursor sapp_get_mouse_cursor(void)
  364. MOUSE LOCK (AKA POINTER LOCK, AKA MOUSE CAPTURE)
  365. ================================================
  366. In normal mouse mode, no mouse movement events are reported when the
  367. mouse leaves the windows client area or hits the screen border (whether
  368. it's one or the other depends on the platform), and the mouse move events
  369. (SAPP_EVENTTYPE_MOUSE_MOVE) contain absolute mouse positions in
  370. framebuffer pixels in the sapp_event items mouse_x and mouse_y, and
  371. relative movement in framebuffer pixels in the sapp_event items mouse_dx
  372. and mouse_dy.
  373. To get continuous mouse movement (also when the mouse leaves the window
  374. client area or hits the screen border), activate mouse-lock mode
  375. by calling:
  376. sapp_lock_mouse(true)
  377. When mouse lock is activated, the mouse pointer is hidden, the
  378. reported absolute mouse position (sapp_event.mouse_x/y) appears
  379. frozen, and the relative mouse movement in sapp_event.mouse_dx/dy
  380. no longer has a direct relation to framebuffer pixels but instead
  381. uses "raw mouse input" (what "raw mouse input" exactly means also
  382. differs by platform).
  383. To deactivate mouse lock and return to normal mouse mode, call
  384. sapp_lock_mouse(false)
  385. And finally, to check if mouse lock is currently active, call
  386. if (sapp_mouse_locked()) { ... }
  387. Note that mouse-lock state may not change immediately after sapp_lock_mouse(true/false)
  388. is called, instead on some platforms the actual state switch may be delayed
  389. to the end of the current frame or even to a later frame.
  390. The mouse may also be unlocked automatically without calling sapp_lock_mouse(false),
  391. most notably when the application window becomes inactive.
  392. On the web platform there are further restrictions to be aware of, caused
  393. by the limitations of the HTML5 Pointer Lock API:
  394. - sapp_lock_mouse(true) can be called at any time, but it will
  395. only take effect in a 'short-lived input event handler of a specific
  396. type', meaning when one of the following events happens:
  397. - SAPP_EVENTTYPE_MOUSE_DOWN
  398. - SAPP_EVENTTYPE_MOUSE_UP
  399. - SAPP_EVENTTYPE_MOUSE_SCROLL
  400. - SAPP_EVENTTYPE_KEY_UP
  401. - SAPP_EVENTTYPE_KEY_DOWN
  402. - The mouse lock/unlock action on the web platform is asynchronous,
  403. this means that sapp_mouse_locked() won't immediately return
  404. the new status after calling sapp_lock_mouse(), instead the
  405. reported status will only change when the pointer lock has actually
  406. been activated or deactivated in the browser.
  407. - On the web, mouse lock can be deactivated by the user at any time
  408. by pressing the Esc key. When this happens, sokol_app.h behaves
  409. the same as if sapp_lock_mouse(false) is called.
  410. For things like camera manipulation it's most straightforward to lock
  411. and unlock the mouse right from the sokol_app.h event handler, for
  412. instance the following code enters and leaves mouse lock when the
  413. left mouse button is pressed and released, and then uses the relative
  414. movement information to manipulate a camera (taken from the
  415. cgltf-sapp.c sample in the sokol-samples repository
  416. at https://github.com/floooh/sokol-samples):
  417. static void input(const sapp_event* ev) {
  418. switch (ev->type) {
  419. case SAPP_EVENTTYPE_MOUSE_DOWN:
  420. if (ev->mouse_button == SAPP_MOUSEBUTTON_LEFT) {
  421. sapp_lock_mouse(true);
  422. }
  423. break;
  424. case SAPP_EVENTTYPE_MOUSE_UP:
  425. if (ev->mouse_button == SAPP_MOUSEBUTTON_LEFT) {
  426. sapp_lock_mouse(false);
  427. }
  428. break;
  429. case SAPP_EVENTTYPE_MOUSE_MOVE:
  430. if (sapp_mouse_locked()) {
  431. cam_orbit(&state.camera, ev->mouse_dx * 0.25f, ev->mouse_dy * 0.25f);
  432. }
  433. break;
  434. default:
  435. break;
  436. }
  437. }
  438. For a 'first person shooter mouse' the following code inside the sokol-app event handler
  439. is recommended somewhere in your frame callback:
  440. if (!sapp_mouse_locked()) {
  441. sapp_lock_mouse(true);
  442. }
  443. CLIPBOARD SUPPORT
  444. =================
  445. Applications can send and receive UTF-8 encoded text data from and to the
  446. system clipboard. By default, clipboard support is disabled and
  447. must be enabled at startup via the following sapp_desc struct
  448. members:
  449. sapp_desc.enable_clipboard - set to true to enable clipboard support
  450. sapp_desc.clipboard_size - size of the internal clipboard buffer in bytes
  451. Enabling the clipboard will dynamically allocate a clipboard buffer
  452. for UTF-8 encoded text data of the requested size in bytes, the default
  453. size is 8 KBytes. Strings that don't fit into the clipboard buffer
  454. (including the terminating zero) will be silently clipped, so it's
  455. important that you provide a big enough clipboard size for your
  456. use case.
  457. To send data to the clipboard, call sapp_set_clipboard_string() with
  458. a pointer to an UTF-8 encoded, null-terminated C-string.
  459. NOTE that on the HTML5 platform, sapp_set_clipboard_string() must be
  460. called from inside a 'short-lived event handler', and there are a few
  461. other HTML5-specific caveats to workaround. You'll basically have to
  462. tinker until it works in all browsers :/ (maybe the situation will
  463. improve when all browsers agree on and implement the new
  464. HTML5 navigator.clipboard API).
  465. To get data from the clipboard, check for the SAPP_EVENTTYPE_CLIPBOARD_PASTED
  466. event in your event handler function, and then call sapp_get_clipboard_string()
  467. to obtain the pasted UTF-8 encoded text.
  468. NOTE that behaviour of sapp_get_clipboard_string() is slightly different
  469. depending on platform:
  470. - on the HTML5 platform, the internal clipboard buffer will only be updated
  471. right before the SAPP_EVENTTYPE_CLIPBOARD_PASTED event is sent,
  472. and sapp_get_clipboard_string() will simply return the current content
  473. of the clipboard buffer
  474. - on 'native' platforms, the call to sapp_get_clipboard_string() will
  475. update the internal clipboard buffer with the most recent data
  476. from the system clipboard
  477. Portable code should check for the SAPP_EVENTTYPE_CLIPBOARD_PASTED event,
  478. and then call sapp_get_clipboard_string() right in the event handler.
  479. The SAPP_EVENTTYPE_CLIPBOARD_PASTED event will be generated by sokol-app
  480. as follows:
  481. - on macOS: when the Cmd+V key is pressed down
  482. - on HTML5: when the browser sends a 'paste' event to the global 'window' object
  483. - on all other platforms: when the Ctrl+V key is pressed down
  484. DRAG AND DROP SUPPORT
  485. =====================
  486. PLEASE NOTE: the drag'n'drop feature works differently on WASM/HTML5
  487. and on the native desktop platforms (Win32, Linux and macOS) because
  488. of security-related restrictions in the HTML5 drag'n'drop API. The
  489. WASM/HTML5 specifics are described at the end of this documentation
  490. section:
  491. Like clipboard support, drag'n'drop support must be explicitly enabled
  492. at startup in the sapp_desc struct.
  493. sapp_desc sokol_main(void) {
  494. return (sapp_desc) {
  495. .enable_dragndrop = true, // default is false
  496. ...
  497. };
  498. }
  499. You can also adjust the maximum number of files that are accepted
  500. in a drop operation, and the maximum path length in bytes if needed:
  501. sapp_desc sokol_main(void) {
  502. return (sapp_desc) {
  503. .enable_dragndrop = true, // default is false
  504. .max_dropped_files = 8, // default is 1
  505. .max_dropped_file_path_length = 8192, // in bytes, default is 2048
  506. ...
  507. };
  508. }
  509. When drag'n'drop is enabled, the event callback will be invoked with an
  510. event of type SAPP_EVENTTYPE_FILES_DROPPED whenever the user drops files on
  511. the application window.
  512. After the SAPP_EVENTTYPE_FILES_DROPPED is received, you can query the
  513. number of dropped files, and their absolute paths by calling separate
  514. functions:
  515. void on_event(const sapp_event* ev) {
  516. if (ev->type == SAPP_EVENTTYPE_FILES_DROPPED) {
  517. // the mouse position where the drop happened
  518. float x = ev->mouse_x;
  519. float y = ev->mouse_y;
  520. // get the number of files and their paths like this:
  521. const int num_dropped_files = sapp_get_num_dropped_files();
  522. for (int i = 0; i < num_dropped_files; i++) {
  523. const char* path = sapp_get_dropped_file_path(i);
  524. ...
  525. }
  526. }
  527. }
  528. The returned file paths are UTF-8 encoded strings.
  529. You can call sapp_get_num_dropped_files() and sapp_get_dropped_file_path()
  530. anywhere, also outside the event handler callback, but be aware that the
  531. file path strings will be overwritten with the next drop operation.
  532. In any case, sapp_get_dropped_file_path() will never return a null pointer,
  533. instead an empty string "" will be returned if the drag'n'drop feature
  534. hasn't been enabled, the last drop-operation failed, or the file path index
  535. is out of range.
  536. Drag'n'drop caveats:
  537. - if more files are dropped in a single drop-action
  538. than sapp_desc.max_dropped_files, the additional
  539. files will be silently ignored
  540. - if any of the file paths is longer than
  541. sapp_desc.max_dropped_file_path_length (in number of bytes, after UTF-8
  542. encoding) the entire drop operation will be silently ignored (this
  543. needs some sort of error feedback in the future)
  544. - no mouse positions are reported while the drag is in
  545. process, this may change in the future
  546. Drag'n'drop on HTML5/WASM:
  547. The HTML5 drag'n'drop API doesn't return file paths, but instead
  548. black-box 'file objects' which must be used to load the content
  549. of dropped files. This is the reason why sokol_app.h adds two
  550. HTML5-specific functions to the drag'n'drop API:
  551. uint32_t sapp_html5_get_dropped_file_size(int index)
  552. Returns the size in bytes of a dropped file.
  553. void sapp_html5_fetch_dropped_file(const sapp_html5_fetch_request* request)
  554. Asynchronously loads the content of a dropped file into a
  555. provided memory buffer (which must be big enough to hold
  556. the file content)
  557. To start loading the first dropped file after an SAPP_EVENTTYPE_FILES_DROPPED
  558. event is received:
  559. sapp_html5_fetch_dropped_file(&(sapp_html5_fetch_request){
  560. .dropped_file_index = 0,
  561. .callback = fetch_cb
  562. .buffer = {
  563. .ptr = buf,
  564. .size = sizeof(buf)
  565. },
  566. .user_data = ...
  567. });
  568. Make sure that the memory pointed to by 'buf' stays valid until the
  569. callback function is called!
  570. As result of the asynchronous loading operation (no matter if succeeded or
  571. failed) the 'fetch_cb' function will be called:
  572. void fetch_cb(const sapp_html5_fetch_response* response) {
  573. // IMPORTANT: check if the loading operation actually succeeded:
  574. if (response->succeeded) {
  575. // the size of the loaded file:
  576. const size_t num_bytes = response->data.size;
  577. // and the pointer to the data (same as 'buf' in the fetch-call):
  578. const void* ptr = response->data.ptr;
  579. } else {
  580. // on error check the error code:
  581. switch (response->error_code) {
  582. case SAPP_HTML5_FETCH_ERROR_BUFFER_TOO_SMALL:
  583. ...
  584. break;
  585. case SAPP_HTML5_FETCH_ERROR_OTHER:
  586. ...
  587. break;
  588. }
  589. }
  590. }
  591. Check the droptest-sapp example for a real-world example which works
  592. both on native platforms and the web:
  593. https://github.com/floooh/sokol-samples/blob/master/sapp/droptest-sapp.c
  594. HIGH-DPI RENDERING
  595. ==================
  596. You can set the sapp_desc.high_dpi flag during initialization to request
  597. a full-resolution framebuffer on HighDPI displays. The default behaviour
  598. is sapp_desc.high_dpi=false, this means that the application will
  599. render to a lower-resolution framebuffer on HighDPI displays and the
  600. rendered content will be upscaled by the window system composer.
  601. In a HighDPI scenario, you still request the same window size during
  602. sokol_main(), but the framebuffer sizes returned by sapp_width()
  603. and sapp_height() will be scaled up according to the DPI scaling
  604. ratio.
  605. Note that on some platforms the DPI scaling factor may change at any
  606. time (for instance when a window is moved from a high-dpi display
  607. to a low-dpi display).
  608. To query the current DPI scaling factor, call the function:
  609. float sapp_dpi_scale(void);
  610. For instance on a Retina Mac, returning the following sapp_desc
  611. struct from sokol_main():
  612. sapp_desc sokol_main(void) {
  613. return (sapp_desc) {
  614. .width = 640,
  615. .height = 480,
  616. .high_dpi = true,
  617. ...
  618. };
  619. }
  620. ...the functions the functions sapp_width(), sapp_height()
  621. and sapp_dpi_scale() will return the following values:
  622. sapp_width: 1280
  623. sapp_height: 960
  624. sapp_dpi_scale: 2.0
  625. If the high_dpi flag is false, or you're not running on a Retina display,
  626. the values would be:
  627. sapp_width: 640
  628. sapp_height: 480
  629. sapp_dpi_scale: 1.0
  630. If the window is moved from the Retina display to a low-dpi external display,
  631. the values would change as follows:
  632. sapp_width: 1280 => 640
  633. sapp_height: 960 => 480
  634. sapp_dpi_scale: 2.0 => 1.0
  635. Currently there is no event associated with a DPI change, but an
  636. SAPP_EVENTTYPE_RESIZED will be sent as a side effect of the
  637. framebuffer size changing.
  638. Per-monitor DPI is currently supported on macOS and Windows.
  639. APPLICATION QUIT
  640. ================
  641. Without special quit handling, a sokol_app.h application will quit
  642. 'gracefully' when the user clicks the window close-button unless a
  643. platform's application model prevents this (e.g. on web or mobile).
  644. 'Graceful exit' means that the application-provided cleanup callback will
  645. be called before the application quits.
  646. On native desktop platforms sokol_app.h provides more control over the
  647. application-quit-process. It's possible to initiate a 'programmatic quit'
  648. from the application code, and a quit initiated by the application user can
  649. be intercepted (for instance to show a custom dialog box).
  650. This 'programmatic quit protocol' is implemented through 3 functions
  651. and 1 event:
  652. - sapp_quit(): This function simply quits the application without
  653. giving the user a chance to intervene. Usually this might
  654. be called when the user clicks the 'Ok' button in a 'Really Quit?'
  655. dialog box
  656. - sapp_request_quit(): Calling sapp_request_quit() will send the
  657. event SAPP_EVENTTYPE_QUIT_REQUESTED to the applications event handler
  658. callback, giving the user code a chance to intervene and cancel the
  659. pending quit process (for instance to show a 'Really Quit?' dialog
  660. box). If the event handler callback does nothing, the application
  661. will be quit as usual. To prevent this, call the function
  662. sapp_cancel_quit() from inside the event handler.
  663. - sapp_cancel_quit(): Cancels a pending quit request, either initiated
  664. by the user clicking the window close button, or programmatically
  665. by calling sapp_request_quit(). The only place where calling this
  666. function makes sense is from inside the event handler callback when
  667. the SAPP_EVENTTYPE_QUIT_REQUESTED event has been received.
  668. - SAPP_EVENTTYPE_QUIT_REQUESTED: this event is sent when the user
  669. clicks the window's close button or application code calls the
  670. sapp_request_quit() function. The event handler callback code can handle
  671. this event by calling sapp_cancel_quit() to cancel the quit.
  672. If the event is ignored, the application will quit as usual.
  673. On the web platform, the quit behaviour differs from native platforms,
  674. because of web-specific restrictions:
  675. A `programmatic quit` initiated by calling sapp_quit() or
  676. sapp_request_quit() will work as described above: the cleanup callback is
  677. called, platform-specific cleanup is performed (on the web
  678. this means that JS event handlers are unregistered), and then
  679. the request-animation-loop will be exited. However that's all. The
  680. web page itself will continue to exist (e.g. it's not possible to
  681. programmatically close the browser tab).
  682. On the web it's also not possible to run custom code when the user
  683. closes a browser tab, so it's not possible to prevent this with a
  684. fancy custom dialog box.
  685. Instead the standard "Leave Site?" dialog box can be activated (or
  686. deactivated) with the following function:
  687. sapp_html5_ask_leave_site(bool ask);
  688. The initial state of the associated internal flag can be provided
  689. at startup via sapp_desc.html5.ask_leave_site.
  690. This feature should only be used sparingly in critical situations - for
  691. instance when the user would loose data - since popping up modal dialog
  692. boxes is considered quite rude in the web world. Note that there's no way
  693. to customize the content of this dialog box or run any code as a result
  694. of the user's decision. Also note that the user must have interacted with
  695. the site before the dialog box will appear. These are all security measures
  696. to prevent fishing.
  697. The Dear ImGui HighDPI sample contains example code of how to
  698. implement a 'Really Quit?' dialog box with Dear ImGui (native desktop
  699. platforms only), and for showing the hardwired "Leave Site?" dialog box
  700. when running on the web platform:
  701. https://floooh.github.io/sokol-html5/wasm/imgui-highdpi-sapp.html
  702. FULLSCREEN
  703. ==========
  704. If the sapp_desc.fullscreen flag is true, sokol-app will try to create
  705. a fullscreen window on platforms with a 'proper' window system
  706. (mobile devices will always use fullscreen). The implementation details
  707. depend on the target platform, in general sokol-app will use a
  708. 'soft approach' which doesn't interfere too much with the platform's
  709. window system (for instance borderless fullscreen window instead of
  710. a 'real' fullscreen mode). Such details might change over time
  711. as sokol-app is adapted for different needs.
  712. The most important effect of fullscreen mode to keep in mind is that
  713. the requested canvas width and height will be ignored for the initial
  714. window size, calling sapp_width() and sapp_height() will instead return
  715. the resolution of the fullscreen canvas (however the provided size
  716. might still be used for the non-fullscreen window, in case the user can
  717. switch back from fullscreen- to windowed-mode).
  718. To toggle fullscreen mode programmatically, call sapp_toggle_fullscreen().
  719. To check if the application window is currently in fullscreen mode,
  720. call sapp_is_fullscreen().
  721. On the web, sapp_desc.fullscreen will have no effect, and the application
  722. will always start in non-fullscreen mode. Call sapp_toggle_fullscreen()
  723. from within or 'near' an input event to switch to fullscreen programatically.
  724. Note that on the web, the fullscreen state may change back to windowed at
  725. any time (either because the browser had rejected switching into fullscreen,
  726. or the user leaves fullscreen via Esc), this means that the result
  727. of sapp_is_fullscreen() may change also without calling sapp_toggle_fullscreen()!
  728. WINDOW ICON SUPPORT
  729. ===================
  730. Some sokol_app.h backends allow to change the window icon programmatically:
  731. - on Win32: the small icon in the window's title bar, and the
  732. bigger icon in the task bar
  733. - on Linux: highly dependent on the used window manager, but usually
  734. the window's title bar icon and/or the task bar icon
  735. - on HTML5: the favicon shown in the page's browser tab
  736. - on macOS: the application icon shown in the dock, but only
  737. for currently running applications
  738. NOTE that it is not possible to set the actual application icon which is
  739. displayed by the operating system on the desktop or 'home screen'. Those
  740. icons must be provided 'traditionally' through operating-system-specific
  741. resources which are associated with the application (sokol_app.h might
  742. later support setting the window icon from platform specific resource data
  743. though).
  744. There are two ways to set the window icon:
  745. - at application start in the sokol_main() function by initializing
  746. the sapp_desc.icon nested struct
  747. - or later by calling the function sapp_set_icon()
  748. As a convenient shortcut, sokol_app.h comes with a builtin default-icon
  749. (a rainbow-colored 'S', which at least looks a bit better than the Windows
  750. default icon for applications), which can be activated like this:
  751. At startup in sokol_main():
  752. sapp_desc sokol_main(...) {
  753. return (sapp_desc){
  754. ...
  755. icon.sokol_default = true
  756. };
  757. }
  758. Or later by calling:
  759. sapp_set_icon(&(sapp_icon_desc){ .sokol_default = true });
  760. NOTE that a completely zero-initialized sapp_icon_desc struct will not
  761. update the window icon in any way. This is an 'escape hatch' so that you
  762. can handle the window icon update yourself (or if you do this already,
  763. sokol_app.h won't get in your way, in this case just leave the
  764. sapp_desc.icon struct zero-initialized).
  765. Providing your own icon images works exactly like in GLFW (down to the
  766. data format):
  767. You provide one or more 'candidate images' in different sizes, and the
  768. sokol_app.h platform backends pick the best match for the specific backend
  769. and icon type.
  770. For each candidate image, you need to provide:
  771. - the width in pixels
  772. - the height in pixels
  773. - and the actual pixel data in RGBA8 pixel format (e.g. 0xFFCC8844
  774. on a little-endian CPU means: alpha=0xFF, blue=0xCC, green=0x88, red=0x44)
  775. For instance, if you have 3 candidate images (small, medium, big) of
  776. sizes 16x16, 32x32 and 64x64 the corresponding sapp_icon_desc struct is setup
  777. like this:
  778. // the actual pixel data (RGBA8, origin top-left)
  779. const uint32_t small[16][16] = { ... };
  780. const uint32_t medium[32][32] = { ... };
  781. const uint32_t big[64][64] = { ... };
  782. const sapp_icon_desc icon_desc = {
  783. .images = {
  784. { .width = 16, .height = 16, .pixels = SAPP_RANGE(small) },
  785. { .width = 32, .height = 32, .pixels = SAPP_RANGE(medium) },
  786. // ...or without the SAPP_RANGE helper macro:
  787. { .width = 64, .height = 64, .pixels = { .ptr=big, .size=sizeof(big) } }
  788. }
  789. };
  790. An sapp_icon_desc struct initialized like this can then either be applied
  791. at application start in sokol_main:
  792. sapp_desc sokol_main(...) {
  793. return (sapp_desc){
  794. ...
  795. icon = icon_desc
  796. };
  797. }
  798. ...or later by calling sapp_set_icon():
  799. sapp_set_icon(&icon_desc);
  800. Some window icon caveats:
  801. - once the window icon has been updated, there's no way to go back to
  802. the platform's default icon, this is because some platforms (Linux
  803. and HTML5) don't switch the icon visual back to the default even if
  804. the custom icon is deleted or removed
  805. - on HTML5, if the sokol_app.h icon doesn't show up in the browser
  806. tab, check that there's no traditional favicon 'link' element
  807. is defined in the page's index.html, sokol_app.h will only
  808. append a new favicon link element, but not delete any manually
  809. defined favicon in the page
  810. For an example and test of the window icon feature, check out the
  811. 'icon-sapp' sample on the sokol-samples git repository.
  812. ONSCREEN KEYBOARD
  813. =================
  814. On some platforms which don't provide a physical keyboard, sokol-app
  815. can display the platform's integrated onscreen keyboard for text
  816. input. To request that the onscreen keyboard is shown, call
  817. sapp_show_keyboard(true);
  818. Likewise, to hide the keyboard call:
  819. sapp_show_keyboard(false);
  820. Note that onscreen keyboard functionality is no longer supported
  821. on the browser platform (the previous hacks and workarounds to make browser
  822. keyboards work for on web applications that don't use HTML UIs
  823. never really worked across browsers).
  824. INPUT EVENT BUBBLING ON THE WEB PLATFORM
  825. ========================================
  826. By default, input event bubbling on the web platform is configured in
  827. a way that makes the most sense for 'full-canvas' apps that cover the
  828. entire browser client window area:
  829. - mouse, touch and wheel events do not bubble up, this prevents various
  830. ugly side events, like:
  831. - HTML text overlays being selected on double- or triple-click into
  832. the canvas
  833. - 'scroll bumping' even when the canvas covers the entire client area
  834. - key_up/down events for 'character keys' *do* bubble up (otherwise
  835. the browser will not generate UNICODE character events)
  836. - all other key events *do not* bubble up by default (this prevents side effects
  837. like F1 opening help, or F7 starting 'caret browsing')
  838. - character events do not bubble up (although I haven't noticed any side effects
  839. otherwise)
  840. Event bubbling can be enabled for input event categories during initialization
  841. in the sapp_desc struct:
  842. sapp_desc sokol_main(int argc, char* argv[]) {
  843. return (sapp_desc){
  844. //...
  845. .html5 = {
  846. .bubble_mouse_events = true,
  847. .bubble_touch_events = true,
  848. .bubble_wheel_events = true,
  849. .bubble_key_events = true,
  850. .bubble_char_events = true,
  851. }
  852. };
  853. }
  854. This basically opens the floodgates and lets *all* input events bubble up to the browser.
  855. To prevent individual events from bubbling, call sapp_consume_event() from within
  856. the sokol_app.h event callback when that specific event is reported.
  857. SETTING THE CANVAS OBJECT ON THE WEB PLATFORM
  858. =============================================
  859. On the web, sokol_app.h and the Emscripten SDK functions need to find
  860. the WebGL/WebGPU canvas intended for rendering and attaching event
  861. handlers. This can happen in four ways:
  862. 1. do nothing and just set the id of the canvas object to 'canvas' (preferred)
  863. 2. via a CSS Selector string (preferred)
  864. 3. by setting the `Module.canvas` property to the canvas object
  865. 4. by adding the canvas object to the global variable `specialHTMLTargets[]`
  866. (this is a special variable used by the Emscripten runtime to lookup
  867. event target objects for which document.querySelector() cannot be used)
  868. The easiest way is to just name your canvas object 'canvas':
  869. <canvas id="canvas" ...></canvas>
  870. This works because the default css selector string used by sokol_app.h
  871. is '#canvas'.
  872. If you name your canvas differently, you need to communicate that name to
  873. sokol_app.h via `sapp_desc.html5.canvas_selector` as a regular css selector
  874. string that's compatible with `document.querySelector()`. E.g. if your canvas
  875. object looks like this:
  876. <canvas id="bla" ...></canvas>
  877. The `sapp_desc.html5.canvas_selector` string must be set to '#bla':
  878. .html5.canvas_selector = "#bla"
  879. If the canvas object cannot be looked up via `document.querySelector()` you
  880. need to use one of the alternative methods, both involve the special
  881. Emscripten runtime `Module` object which is usually setup in the index.html
  882. like this before the WASM blob is loaded and instantiated:
  883. <script type='text/javascript'>
  884. var Module = {
  885. // ...
  886. };
  887. </script>
  888. The first option is to set the `Module.canvas` property to your canvas object:
  889. <script type='text/javascript'>
  890. var Module = {
  891. canvas: my_canvas_object,
  892. };
  893. </script>
  894. When sokol_app.h initializes, it will check the global Module object whether
  895. a `Module.canvas` property exists and is an object. This method will add
  896. a new entry to the `specialHTMLTargets[]` object
  897. The other option is to add the canvas under a name chosen by you to the
  898. special `specialHTMLTargets[]` map, which is used by the Emscripten runtime
  899. to lookup 'event target objects' which are not visible to `document.querySelector()`.
  900. Note that `specialHTMLTargets[]` must be updated after the Emscripten runtime
  901. has started but before the WASM code is running. A good place for this is
  902. the special `Module.preRun` array in index.html:
  903. <script type='text/javascript'>
  904. var Module = {
  905. preRun: [
  906. () => {
  907. specialHTMLTargets['my_canvas'] = my_canvas_object;
  908. }
  909. ],
  910. };
  911. </script>
  912. In that case, pass the same string to sokol_app.h which is used as key
  913. in the specialHTMLTargets[] map:
  914. .html5.canvas_selector = "my_canvas"
  915. If sokol_app.h can't find your canvas for some reason check for warning
  916. messages on the browser console.
  917. OPTIONAL: DON'T HIJACK main() (#define SOKOL_NO_ENTRY)
  918. ======================================================
  919. NOTE: SOKOL_NO_ENTRY and sapp_run() is currently not supported on Android.
  920. In its default configuration, sokol_app.h "hijacks" the platform's
  921. standard main() function. This was done because different platforms
  922. have different entry point conventions which are not compatible with
  923. C's main() (for instance WinMain on Windows has completely different
  924. arguments). However, this "main hijacking" posed a problem for
  925. usage scenarios like integrating sokol_app.h with other languages than
  926. C or C++, so an alternative SOKOL_NO_ENTRY mode has been added
  927. in which the user code provides the platform's main function:
  928. - define SOKOL_NO_ENTRY before including the sokol_app.h implementation
  929. - do *not* provide a sokol_main() function
  930. - instead provide the standard main() function of the platform
  931. - from the main function, call the function ```sapp_run()``` which
  932. takes a pointer to an ```sapp_desc``` structure.
  933. - from here on```sapp_run()``` takes over control and calls the provided
  934. init-, frame-, event- and cleanup-callbacks just like in the default model.
  935. sapp_run() behaves differently across platforms:
  936. - on some platforms, sapp_run() will return when the application quits
  937. - on other platforms, sapp_run() will never return, even when the
  938. application quits (the operating system is free to simply terminate
  939. the application at any time)
  940. - on Emscripten specifically, sapp_run() will return immediately while
  941. the frame callback keeps being called
  942. This different behaviour of sapp_run() essentially means that there shouldn't
  943. be any code *after* sapp_run(), because that may either never be called, or in
  944. case of Emscripten will be called at an unexpected time (at application start).
  945. An application also should not depend on the cleanup-callback being called
  946. when cross-platform compatibility is required.
  947. Since sapp_run() returns immediately on Emscripten you shouldn't activate
  948. the 'EXIT_RUNTIME' linker option (this is disabled by default when compiling
  949. for the browser target), since the C/C++ exit runtime would be called immediately at
  950. application start, causing any global objects to be destroyed and global
  951. variables to be zeroed.
  952. WINDOWS CONSOLE OUTPUT
  953. ======================
  954. On Windows, regular windowed applications don't show any stdout/stderr text
  955. output, which can be a bit of a hassle for printf() debugging or generally
  956. logging text to the console. Also, console output by default uses a local
  957. codepage setting and thus international UTF-8 encoded text is printed
  958. as garbage.
  959. To help with these issues, sokol_app.h can be configured at startup
  960. via the following Windows-specific sapp_desc flags:
  961. sapp_desc.win32.console_utf8 (default: false)
  962. When set to true, the output console codepage will be switched
  963. to UTF-8 (and restored to the original codepage on exit)
  964. sapp_desc.win32.console_attach (default: false)
  965. When set to true, stdout and stderr will be attached to the
  966. console of the parent process (if the parent process actually
  967. has a console). This means that if the application was started
  968. in a command line window, stdout and stderr output will be printed
  969. to the terminal, just like a regular command line program. But if
  970. the application is started via double-click, it will behave like
  971. a regular UI application, and stdout/stderr will not be visible.
  972. sapp_desc.win32.console_create (default: false)
  973. When set to true, a new console window will be created and
  974. stdout/stderr will be redirected to that console window. It
  975. doesn't matter if the application is started from the command
  976. line or via double-click.
  977. NOTE: setting both win32.console_attach and win32.console_create
  978. to true also makes sense and has the effect that output
  979. will appear in the existing terminal when started from the cmdline, and
  980. otherwise (when started via double-click) will open a console window.
  981. MEMORY ALLOCATION OVERRIDE
  982. ==========================
  983. You can override the memory allocation functions at initialization time
  984. like this:
  985. void* my_alloc(size_t size, void* user_data) {
  986. return malloc(size);
  987. }
  988. void my_free(void* ptr, void* user_data) {
  989. free(ptr);
  990. }
  991. sapp_desc sokol_main(int argc, char* argv[]) {
  992. return (sapp_desc){
  993. // ...
  994. .allocator = {
  995. .alloc_fn = my_alloc,
  996. .free_fn = my_free,
  997. .user_data = ...,
  998. }
  999. };
  1000. }
  1001. If no overrides are provided, malloc and free will be used.
  1002. This only affects memory allocation calls done by sokol_app.h
  1003. itself though, not any allocations in OS libraries.
  1004. ERROR REPORTING AND LOGGING
  1005. ===========================
  1006. To get any logging information at all you need to provide a logging callback in the setup call
  1007. the easiest way is to use sokol_log.h:
  1008. #include "sokol_log.h"
  1009. sapp_desc sokol_main(int argc, char* argv[]) {
  1010. return (sapp_desc) {
  1011. ...
  1012. .logger.func = slog_func,
  1013. };
  1014. }
  1015. To override logging with your own callback, first write a logging function like this:
  1016. void my_log(const char* tag, // e.g. 'sapp'
  1017. uint32_t log_level, // 0=panic, 1=error, 2=warn, 3=info
  1018. uint32_t log_item_id, // SAPP_LOGITEM_*
  1019. const char* message_or_null, // a message string, may be nullptr in release mode
  1020. uint32_t line_nr, // line number in sokol_app.h
  1021. const char* filename_or_null, // source filename, may be nullptr in release mode
  1022. void* user_data)
  1023. {
  1024. ...
  1025. }
  1026. ...and then setup sokol-app like this:
  1027. sapp_desc sokol_main(int argc, char* argv[]) {
  1028. return (sapp_desc) {
  1029. ...
  1030. .logger = {
  1031. .func = my_log,
  1032. .user_data = my_user_data,
  1033. }
  1034. };
  1035. }
  1036. The provided logging function must be reentrant (e.g. be callable from
  1037. different threads).
  1038. If you don't want to provide your own custom logger it is highly recommended to use
  1039. the standard logger in sokol_log.h instead, otherwise you won't see any warnings or
  1040. errors.
  1041. TEMP NOTE DUMP
  1042. ==============
  1043. - sapp_desc needs a bool whether to initialize depth-stencil surface
  1044. - the Android implementation calls cleanup_cb() and destroys the egl context in onDestroy
  1045. at the latest but should do it earlier, in onStop, as an app is "killable" after onStop
  1046. on Android Honeycomb and later (it can't be done at the moment as the app may be started
  1047. again after onStop and the sokol lifecycle does not yet handle context teardown/bringup)
  1048. LICENSE
  1049. =======
  1050. zlib/libpng license
  1051. Copyright (c) 2018 Andre Weissflog
  1052. This software is provided 'as-is', without any express or implied warranty.
  1053. In no event will the authors be held liable for any damages arising from the
  1054. use of this software.
  1055. Permission is granted to anyone to use this software for any purpose,
  1056. including commercial applications, and to alter it and redistribute it
  1057. freely, subject to the following restrictions:
  1058. 1. The origin of this software must not be misrepresented; you must not
  1059. claim that you wrote the original software. If you use this software in a
  1060. product, an acknowledgment in the product documentation would be
  1061. appreciated but is not required.
  1062. 2. Altered source versions must be plainly marked as such, and must not
  1063. be misrepresented as being the original software.
  1064. 3. This notice may not be removed or altered from any source
  1065. distribution.
  1066. */
  1067. #define SOKOL_APP_INCLUDED (1)
  1068. #include <stddef.h> // size_t
  1069. #include <stdint.h>
  1070. #include <stdbool.h>
  1071. #if defined(SOKOL_API_DECL) && !defined(SOKOL_APP_API_DECL)
  1072. #define SOKOL_APP_API_DECL SOKOL_API_DECL
  1073. #endif
  1074. #ifndef SOKOL_APP_API_DECL
  1075. #if defined(_WIN32) && defined(SOKOL_DLL) && defined(SOKOL_APP_IMPL)
  1076. #define SOKOL_APP_API_DECL __declspec(dllexport)
  1077. #elif defined(_WIN32) && defined(SOKOL_DLL)
  1078. #define SOKOL_APP_API_DECL __declspec(dllimport)
  1079. #else
  1080. #define SOKOL_APP_API_DECL extern
  1081. #endif
  1082. #endif
  1083. #ifdef __cplusplus
  1084. extern "C" {
  1085. #endif
  1086. /* misc constants */
  1087. enum {
  1088. SAPP_MAX_TOUCHPOINTS = 8,
  1089. SAPP_MAX_MOUSEBUTTONS = 3,
  1090. SAPP_MAX_KEYCODES = 512,
  1091. SAPP_MAX_ICONIMAGES = 8,
  1092. };
  1093. /*
  1094. sapp_event_type
  1095. The type of event that's passed to the event handler callback
  1096. in the sapp_event.type field. These are not just "traditional"
  1097. input events, but also notify the application about state changes
  1098. or other user-invoked actions.
  1099. */
  1100. typedef enum sapp_event_type {
  1101. SAPP_EVENTTYPE_INVALID,
  1102. SAPP_EVENTTYPE_KEY_DOWN,
  1103. SAPP_EVENTTYPE_KEY_UP,
  1104. SAPP_EVENTTYPE_CHAR,
  1105. SAPP_EVENTTYPE_MOUSE_DOWN,
  1106. SAPP_EVENTTYPE_MOUSE_UP,
  1107. SAPP_EVENTTYPE_MOUSE_SCROLL,
  1108. SAPP_EVENTTYPE_MOUSE_MOVE,
  1109. SAPP_EVENTTYPE_MOUSE_ENTER,
  1110. SAPP_EVENTTYPE_MOUSE_LEAVE,
  1111. SAPP_EVENTTYPE_TOUCHES_BEGAN,
  1112. SAPP_EVENTTYPE_TOUCHES_MOVED,
  1113. SAPP_EVENTTYPE_TOUCHES_ENDED,
  1114. SAPP_EVENTTYPE_TOUCHES_CANCELLED,
  1115. SAPP_EVENTTYPE_RESIZED,
  1116. SAPP_EVENTTYPE_ICONIFIED,
  1117. SAPP_EVENTTYPE_RESTORED,
  1118. SAPP_EVENTTYPE_FOCUSED,
  1119. SAPP_EVENTTYPE_UNFOCUSED,
  1120. SAPP_EVENTTYPE_SUSPENDED,
  1121. SAPP_EVENTTYPE_RESUMED,
  1122. SAPP_EVENTTYPE_QUIT_REQUESTED,
  1123. SAPP_EVENTTYPE_CLIPBOARD_PASTED,
  1124. SAPP_EVENTTYPE_FILES_DROPPED,
  1125. _SAPP_EVENTTYPE_NUM,
  1126. _SAPP_EVENTTYPE_FORCE_U32 = 0x7FFFFFFF
  1127. } sapp_event_type;
  1128. /*
  1129. sapp_keycode
  1130. The 'virtual keycode' of a KEY_DOWN or KEY_UP event in the
  1131. struct field sapp_event.key_code.
  1132. Note that the keycode values are identical with GLFW.
  1133. */
  1134. typedef enum sapp_keycode {
  1135. SAPP_KEYCODE_INVALID = 0,
  1136. SAPP_KEYCODE_SPACE = 32,
  1137. SAPP_KEYCODE_APOSTROPHE = 39, /* ' */
  1138. SAPP_KEYCODE_COMMA = 44, /* , */
  1139. SAPP_KEYCODE_MINUS = 45, /* - */
  1140. SAPP_KEYCODE_PERIOD = 46, /* . */
  1141. SAPP_KEYCODE_SLASH = 47, /* / */
  1142. SAPP_KEYCODE_0 = 48,
  1143. SAPP_KEYCODE_1 = 49,
  1144. SAPP_KEYCODE_2 = 50,
  1145. SAPP_KEYCODE_3 = 51,
  1146. SAPP_KEYCODE_4 = 52,
  1147. SAPP_KEYCODE_5 = 53,
  1148. SAPP_KEYCODE_6 = 54,
  1149. SAPP_KEYCODE_7 = 55,
  1150. SAPP_KEYCODE_8 = 56,
  1151. SAPP_KEYCODE_9 = 57,
  1152. SAPP_KEYCODE_SEMICOLON = 59, /* ; */
  1153. SAPP_KEYCODE_EQUAL = 61, /* = */
  1154. SAPP_KEYCODE_A = 65,
  1155. SAPP_KEYCODE_B = 66,
  1156. SAPP_KEYCODE_C = 67,
  1157. SAPP_KEYCODE_D = 68,
  1158. SAPP_KEYCODE_E = 69,
  1159. SAPP_KEYCODE_F = 70,
  1160. SAPP_KEYCODE_G = 71,
  1161. SAPP_KEYCODE_H = 72,
  1162. SAPP_KEYCODE_I = 73,
  1163. SAPP_KEYCODE_J = 74,
  1164. SAPP_KEYCODE_K = 75,
  1165. SAPP_KEYCODE_L = 76,
  1166. SAPP_KEYCODE_M = 77,
  1167. SAPP_KEYCODE_N = 78,
  1168. SAPP_KEYCODE_O = 79,
  1169. SAPP_KEYCODE_P = 80,
  1170. SAPP_KEYCODE_Q = 81,
  1171. SAPP_KEYCODE_R = 82,
  1172. SAPP_KEYCODE_S = 83,
  1173. SAPP_KEYCODE_T = 84,
  1174. SAPP_KEYCODE_U = 85,
  1175. SAPP_KEYCODE_V = 86,
  1176. SAPP_KEYCODE_W = 87,
  1177. SAPP_KEYCODE_X = 88,
  1178. SAPP_KEYCODE_Y = 89,
  1179. SAPP_KEYCODE_Z = 90,
  1180. SAPP_KEYCODE_LEFT_BRACKET = 91, /* [ */
  1181. SAPP_KEYCODE_BACKSLASH = 92, /* \ */
  1182. SAPP_KEYCODE_RIGHT_BRACKET = 93, /* ] */
  1183. SAPP_KEYCODE_GRAVE_ACCENT = 96, /* ` */
  1184. SAPP_KEYCODE_WORLD_1 = 161, /* non-US #1 */
  1185. SAPP_KEYCODE_WORLD_2 = 162, /* non-US #2 */
  1186. SAPP_KEYCODE_ESCAPE = 256,
  1187. SAPP_KEYCODE_ENTER = 257,
  1188. SAPP_KEYCODE_TAB = 258,
  1189. SAPP_KEYCODE_BACKSPACE = 259,
  1190. SAPP_KEYCODE_INSERT = 260,
  1191. SAPP_KEYCODE_DELETE = 261,
  1192. SAPP_KEYCODE_RIGHT = 262,
  1193. SAPP_KEYCODE_LEFT = 263,
  1194. SAPP_KEYCODE_DOWN = 264,
  1195. SAPP_KEYCODE_UP = 265,
  1196. SAPP_KEYCODE_PAGE_UP = 266,
  1197. SAPP_KEYCODE_PAGE_DOWN = 267,
  1198. SAPP_KEYCODE_HOME = 268,
  1199. SAPP_KEYCODE_END = 269,
  1200. SAPP_KEYCODE_CAPS_LOCK = 280,
  1201. SAPP_KEYCODE_SCROLL_LOCK = 281,
  1202. SAPP_KEYCODE_NUM_LOCK = 282,
  1203. SAPP_KEYCODE_PRINT_SCREEN = 283,
  1204. SAPP_KEYCODE_PAUSE = 284,
  1205. SAPP_KEYCODE_F1 = 290,
  1206. SAPP_KEYCODE_F2 = 291,
  1207. SAPP_KEYCODE_F3 = 292,
  1208. SAPP_KEYCODE_F4 = 293,
  1209. SAPP_KEYCODE_F5 = 294,
  1210. SAPP_KEYCODE_F6 = 295,
  1211. SAPP_KEYCODE_F7 = 296,
  1212. SAPP_KEYCODE_F8 = 297,
  1213. SAPP_KEYCODE_F9 = 298,
  1214. SAPP_KEYCODE_F10 = 299,
  1215. SAPP_KEYCODE_F11 = 300,
  1216. SAPP_KEYCODE_F12 = 301,
  1217. SAPP_KEYCODE_F13 = 302,
  1218. SAPP_KEYCODE_F14 = 303,
  1219. SAPP_KEYCODE_F15 = 304,
  1220. SAPP_KEYCODE_F16 = 305,
  1221. SAPP_KEYCODE_F17 = 306,
  1222. SAPP_KEYCODE_F18 = 307,
  1223. SAPP_KEYCODE_F19 = 308,
  1224. SAPP_KEYCODE_F20 = 309,
  1225. SAPP_KEYCODE_F21 = 310,
  1226. SAPP_KEYCODE_F22 = 311,
  1227. SAPP_KEYCODE_F23 = 312,
  1228. SAPP_KEYCODE_F24 = 313,
  1229. SAPP_KEYCODE_F25 = 314,
  1230. SAPP_KEYCODE_KP_0 = 320,
  1231. SAPP_KEYCODE_KP_1 = 321,
  1232. SAPP_KEYCODE_KP_2 = 322,
  1233. SAPP_KEYCODE_KP_3 = 323,
  1234. SAPP_KEYCODE_KP_4 = 324,
  1235. SAPP_KEYCODE_KP_5 = 325,
  1236. SAPP_KEYCODE_KP_6 = 326,
  1237. SAPP_KEYCODE_KP_7 = 327,
  1238. SAPP_KEYCODE_KP_8 = 328,
  1239. SAPP_KEYCODE_KP_9 = 329,
  1240. SAPP_KEYCODE_KP_DECIMAL = 330,
  1241. SAPP_KEYCODE_KP_DIVIDE = 331,
  1242. SAPP_KEYCODE_KP_MULTIPLY = 332,
  1243. SAPP_KEYCODE_KP_SUBTRACT = 333,
  1244. SAPP_KEYCODE_KP_ADD = 334,
  1245. SAPP_KEYCODE_KP_ENTER = 335,
  1246. SAPP_KEYCODE_KP_EQUAL = 336,
  1247. SAPP_KEYCODE_LEFT_SHIFT = 340,
  1248. SAPP_KEYCODE_LEFT_CONTROL = 341,
  1249. SAPP_KEYCODE_LEFT_ALT = 342,
  1250. SAPP_KEYCODE_LEFT_SUPER = 343,
  1251. SAPP_KEYCODE_RIGHT_SHIFT = 344,
  1252. SAPP_KEYCODE_RIGHT_CONTROL = 345,
  1253. SAPP_KEYCODE_RIGHT_ALT = 346,
  1254. SAPP_KEYCODE_RIGHT_SUPER = 347,
  1255. SAPP_KEYCODE_MENU = 348,
  1256. } sapp_keycode;
  1257. /*
  1258. Android specific 'tool type' enum for touch events. This lets the
  1259. application check what type of input device was used for
  1260. touch events.
  1261. NOTE: the values must remain in sync with the corresponding
  1262. Android SDK type, so don't change those.
  1263. See https://developer.android.com/reference/android/view/MotionEvent#TOOL_TYPE_UNKNOWN
  1264. */
  1265. typedef enum sapp_android_tooltype {
  1266. SAPP_ANDROIDTOOLTYPE_UNKNOWN = 0, // TOOL_TYPE_UNKNOWN
  1267. SAPP_ANDROIDTOOLTYPE_FINGER = 1, // TOOL_TYPE_FINGER
  1268. SAPP_ANDROIDTOOLTYPE_STYLUS = 2, // TOOL_TYPE_STYLUS
  1269. SAPP_ANDROIDTOOLTYPE_MOUSE = 3, // TOOL_TYPE_MOUSE
  1270. } sapp_android_tooltype;
  1271. /*
  1272. sapp_touchpoint
  1273. Describes a single touchpoint in a multitouch event (TOUCHES_BEGAN,
  1274. TOUCHES_MOVED, TOUCHES_ENDED).
  1275. Touch points are stored in the nested array sapp_event.touches[],
  1276. and the number of touches is stored in sapp_event.num_touches.
  1277. */
  1278. typedef struct sapp_touchpoint {
  1279. uintptr_t identifier;
  1280. float pos_x;
  1281. float pos_y;
  1282. sapp_android_tooltype android_tooltype; // only valid on Android
  1283. bool changed;
  1284. } sapp_touchpoint;
  1285. /*
  1286. sapp_mousebutton
  1287. The currently pressed mouse button in the events MOUSE_DOWN
  1288. and MOUSE_UP, stored in the struct field sapp_event.mouse_button.
  1289. */
  1290. typedef enum sapp_mousebutton {
  1291. SAPP_MOUSEBUTTON_LEFT = 0x0,
  1292. SAPP_MOUSEBUTTON_RIGHT = 0x1,
  1293. SAPP_MOUSEBUTTON_MIDDLE = 0x2,
  1294. SAPP_MOUSEBUTTON_INVALID = 0x100,
  1295. } sapp_mousebutton;
  1296. /*
  1297. These are currently pressed modifier keys (and mouse buttons) which are
  1298. passed in the event struct field sapp_event.modifiers.
  1299. */
  1300. enum {
  1301. SAPP_MODIFIER_SHIFT = 0x1, // left or right shift key
  1302. SAPP_MODIFIER_CTRL = 0x2, // left or right control key
  1303. SAPP_MODIFIER_ALT = 0x4, // left or right alt key
  1304. SAPP_MODIFIER_SUPER = 0x8, // left or right 'super' key
  1305. SAPP_MODIFIER_LMB = 0x100, // left mouse button
  1306. SAPP_MODIFIER_RMB = 0x200, // right mouse button
  1307. SAPP_MODIFIER_MMB = 0x400, // middle mouse button
  1308. };
  1309. /*
  1310. sapp_event
  1311. This is an all-in-one event struct passed to the event handler
  1312. user callback function. Note that it depends on the event
  1313. type what struct fields actually contain useful values, so you
  1314. should first check the event type before reading other struct
  1315. fields.
  1316. */
  1317. typedef struct sapp_event {
  1318. uint64_t frame_count; // current frame counter, always valid, useful for checking if two events were issued in the same frame
  1319. sapp_event_type type; // the event type, always valid
  1320. sapp_keycode key_code; // the virtual key code, only valid in KEY_UP, KEY_DOWN
  1321. uint32_t char_code; // the UTF-32 character code, only valid in CHAR events
  1322. bool key_repeat; // true if this is a key-repeat event, valid in KEY_UP, KEY_DOWN and CHAR
  1323. uint32_t modifiers; // current modifier keys, valid in all key-, char- and mouse-events
  1324. sapp_mousebutton mouse_button; // mouse button that was pressed or released, valid in MOUSE_DOWN, MOUSE_UP
  1325. float mouse_x; // current horizontal mouse position in pixels, always valid except during mouse lock
  1326. float mouse_y; // current vertical mouse position in pixels, always valid except during mouse lock
  1327. float mouse_dx; // relative horizontal mouse movement since last frame, always valid
  1328. float mouse_dy; // relative vertical mouse movement since last frame, always valid
  1329. float scroll_x; // horizontal mouse wheel scroll distance, valid in MOUSE_SCROLL events
  1330. float scroll_y; // vertical mouse wheel scroll distance, valid in MOUSE_SCROLL events
  1331. int num_touches; // number of valid items in the touches[] array
  1332. sapp_touchpoint touches[SAPP_MAX_TOUCHPOINTS]; // current touch points, valid in TOUCHES_BEGIN, TOUCHES_MOVED, TOUCHES_ENDED
  1333. int window_width; // current window- and framebuffer sizes in pixels, always valid
  1334. int window_height;
  1335. int framebuffer_width; // = window_width * dpi_scale
  1336. int framebuffer_height; // = window_height * dpi_scale
  1337. } sapp_event;
  1338. /*
  1339. sg_range
  1340. A general pointer/size-pair struct and constructor macros for passing binary blobs
  1341. into sokol_app.h.
  1342. */
  1343. typedef struct sapp_range {
  1344. const void* ptr;
  1345. size_t size;
  1346. } sapp_range;
  1347. // disabling this for every includer isn't great, but the warnings are also quite pointless
  1348. #if defined(_MSC_VER)
  1349. #pragma warning(disable:4221) /* /W4 only: nonstandard extension used: 'x': cannot be initialized using address of automatic variable 'y' */
  1350. #pragma warning(disable:4204) /* VS2015: nonstandard extension used: non-constant aggregate initializer */
  1351. #endif
  1352. #if defined(__cplusplus)
  1353. #define SAPP_RANGE(x) sapp_range{ &x, sizeof(x) }
  1354. #else
  1355. #define SAPP_RANGE(x) (sapp_range){ &x, sizeof(x) }
  1356. #endif
  1357. /*
  1358. sapp_image_desc
  1359. This is used to describe image data to sokol_app.h (window icons and cursor images).
  1360. The pixel format is RGBA8.
  1361. cursor_hotspot_x and _y are used only for cursors, to define which pixel
  1362. of the image should be aligned with the mouse position.
  1363. */
  1364. typedef struct sapp_image_desc {
  1365. int width;
  1366. int height;
  1367. int cursor_hotspot_x;
  1368. int cursor_hotspot_y;
  1369. sapp_range pixels;
  1370. } sapp_image_desc;
  1371. /*
  1372. sapp_icon_desc
  1373. An icon description structure for use in sapp_desc.icon and
  1374. sapp_set_icon().
  1375. When setting a custom image, the application can provide a number of
  1376. candidates differing in size, and sokol_app.h will pick the image(s)
  1377. closest to the size expected by the platform's window system.
  1378. To set sokol-app's default icon, set .sokol_default to true.
  1379. Otherwise provide candidate images of different sizes in the
  1380. images[] array.
  1381. If both the sokol_default flag is set to true, any image candidates
  1382. will be ignored and the sokol_app.h default icon will be set.
  1383. */
  1384. typedef struct sapp_icon_desc {
  1385. bool sokol_default;
  1386. sapp_image_desc images[SAPP_MAX_ICONIMAGES];
  1387. } sapp_icon_desc;
  1388. /*
  1389. sapp_allocator
  1390. Used in sapp_desc to provide custom memory-alloc and -free functions
  1391. to sokol_app.h. If memory management should be overridden, both the
  1392. alloc_fn and free_fn function must be provided (e.g. it's not valid to
  1393. override one function but not the other).
  1394. */
  1395. typedef struct sapp_allocator {
  1396. void* (*alloc_fn)(size_t size, void* user_data);
  1397. void (*free_fn)(void* ptr, void* user_data);
  1398. void* user_data;
  1399. } sapp_allocator;
  1400. /*
  1401. sapp_log_item
  1402. Log items are defined via X-Macros and expanded to an enum
  1403. 'sapp_log_item', and in debug mode to corresponding
  1404. human readable error messages.
  1405. */
  1406. #define _SAPP_LOG_ITEMS \
  1407. _SAPP_LOGITEM_XMACRO(OK, "Ok") \
  1408. _SAPP_LOGITEM_XMACRO(MALLOC_FAILED, "memory allocation failed") \
  1409. _SAPP_LOGITEM_XMACRO(MACOS_INVALID_NSOPENGL_PROFILE, "macos: invalid NSOpenGLProfile (valid choices are 1.0 and 4.1)") \
  1410. _SAPP_LOGITEM_XMACRO(WIN32_LOAD_OPENGL32_DLL_FAILED, "failed loading opengl32.dll") \
  1411. _SAPP_LOGITEM_XMACRO(WIN32_CREATE_HELPER_WINDOW_FAILED, "failed to create helper window") \
  1412. _SAPP_LOGITEM_XMACRO(WIN32_HELPER_WINDOW_GETDC_FAILED, "failed to get helper window DC") \
  1413. _SAPP_LOGITEM_XMACRO(WIN32_DUMMY_CONTEXT_SET_PIXELFORMAT_FAILED, "failed to set pixel format for dummy GL context") \
  1414. _SAPP_LOGITEM_XMACRO(WIN32_CREATE_DUMMY_CONTEXT_FAILED, "failed to create dummy GL context") \
  1415. _SAPP_LOGITEM_XMACRO(WIN32_DUMMY_CONTEXT_MAKE_CURRENT_FAILED, "failed to make dummy GL context current") \
  1416. _SAPP_LOGITEM_XMACRO(WIN32_GET_PIXELFORMAT_ATTRIB_FAILED, "failed to get WGL pixel format attribute") \
  1417. _SAPP_LOGITEM_XMACRO(WIN32_WGL_FIND_PIXELFORMAT_FAILED, "failed to find matching WGL pixel format") \
  1418. _SAPP_LOGITEM_XMACRO(WIN32_WGL_DESCRIBE_PIXELFORMAT_FAILED, "failed to get pixel format descriptor") \
  1419. _SAPP_LOGITEM_XMACRO(WIN32_WGL_SET_PIXELFORMAT_FAILED, "failed to set selected pixel format") \
  1420. _SAPP_LOGITEM_XMACRO(WIN32_WGL_ARB_CREATE_CONTEXT_REQUIRED, "ARB_create_context required") \
  1421. _SAPP_LOGITEM_XMACRO(WIN32_WGL_ARB_CREATE_CONTEXT_PROFILE_REQUIRED, "ARB_create_context_profile required") \
  1422. _SAPP_LOGITEM_XMACRO(WIN32_WGL_OPENGL_VERSION_NOT_SUPPORTED, "requested OpenGL version not supported by GL driver (ERROR_INVALID_VERSION_ARB)") \
  1423. _SAPP_LOGITEM_XMACRO(WIN32_WGL_OPENGL_PROFILE_NOT_SUPPORTED, "requested OpenGL profile not support by GL driver (ERROR_INVALID_PROFILE_ARB)") \
  1424. _SAPP_LOGITEM_XMACRO(WIN32_WGL_INCOMPATIBLE_DEVICE_CONTEXT, "CreateContextAttribsARB failed with ERROR_INCOMPATIBLE_DEVICE_CONTEXTS_ARB") \
  1425. _SAPP_LOGITEM_XMACRO(WIN32_WGL_CREATE_CONTEXT_ATTRIBS_FAILED_OTHER, "CreateContextAttribsARB failed for other reason") \
  1426. _SAPP_LOGITEM_XMACRO(WIN32_D3D11_CREATE_DEVICE_AND_SWAPCHAIN_WITH_DEBUG_FAILED, "D3D11CreateDeviceAndSwapChain() with D3D11_CREATE_DEVICE_DEBUG failed, retrying without debug flag.") \
  1427. _SAPP_LOGITEM_XMACRO(WIN32_D3D11_GET_IDXGIFACTORY_FAILED, "could not obtain IDXGIFactory object") \
  1428. _SAPP_LOGITEM_XMACRO(WIN32_D3D11_GET_IDXGIADAPTER_FAILED, "could not obtain IDXGIAdapter object") \
  1429. _SAPP_LOGITEM_XMACRO(WIN32_D3D11_QUERY_INTERFACE_IDXGIDEVICE1_FAILED, "could not obtain IDXGIDevice1 interface") \
  1430. _SAPP_LOGITEM_XMACRO(WIN32_REGISTER_RAW_INPUT_DEVICES_FAILED_MOUSE_LOCK, "RegisterRawInputDevices() failed (on mouse lock)") \
  1431. _SAPP_LOGITEM_XMACRO(WIN32_REGISTER_RAW_INPUT_DEVICES_FAILED_MOUSE_UNLOCK, "RegisterRawInputDevices() failed (on mouse unlock)") \
  1432. _SAPP_LOGITEM_XMACRO(WIN32_GET_RAW_INPUT_DATA_FAILED, "GetRawInputData() failed") \
  1433. _SAPP_LOGITEM_XMACRO(WIN32_DESTROYICON_FOR_CURSOR_FAILED, "DestroyIcon() for a cursor image failed") \
  1434. _SAPP_LOGITEM_XMACRO(LINUX_GLX_LOAD_LIBGL_FAILED, "failed to load libGL") \
  1435. _SAPP_LOGITEM_XMACRO(LINUX_GLX_LOAD_ENTRY_POINTS_FAILED, "failed to load GLX entry points") \
  1436. _SAPP_LOGITEM_XMACRO(LINUX_GLX_EXTENSION_NOT_FOUND, "GLX extension not found") \
  1437. _SAPP_LOGITEM_XMACRO(LINUX_GLX_QUERY_VERSION_FAILED, "failed to query GLX version") \
  1438. _SAPP_LOGITEM_XMACRO(LINUX_GLX_VERSION_TOO_LOW, "GLX version too low (need at least 1.3)") \
  1439. _SAPP_LOGITEM_XMACRO(LINUX_GLX_NO_GLXFBCONFIGS, "glXGetFBConfigs() returned no configs") \
  1440. _SAPP_LOGITEM_XMACRO(LINUX_GLX_NO_SUITABLE_GLXFBCONFIG, "failed to find a suitable GLXFBConfig") \
  1441. _SAPP_LOGITEM_XMACRO(LINUX_GLX_GET_VISUAL_FROM_FBCONFIG_FAILED, "glXGetVisualFromFBConfig failed") \
  1442. _SAPP_LOGITEM_XMACRO(LINUX_GLX_REQUIRED_EXTENSIONS_MISSING, "GLX extensions ARB_create_context and ARB_create_context_profile missing") \
  1443. _SAPP_LOGITEM_XMACRO(LINUX_GLX_CREATE_CONTEXT_FAILED, "Failed to create GL context via glXCreateContextAttribsARB") \
  1444. _SAPP_LOGITEM_XMACRO(LINUX_GLX_CREATE_WINDOW_FAILED, "glXCreateWindow() failed") \
  1445. _SAPP_LOGITEM_XMACRO(LINUX_X11_CREATE_WINDOW_FAILED, "XCreateWindow() failed") \
  1446. _SAPP_LOGITEM_XMACRO(LINUX_EGL_BIND_OPENGL_API_FAILED, "eglBindAPI(EGL_OPENGL_API) failed") \
  1447. _SAPP_LOGITEM_XMACRO(LINUX_EGL_BIND_OPENGL_ES_API_FAILED, "eglBindAPI(EGL_OPENGL_ES_API) failed") \
  1448. _SAPP_LOGITEM_XMACRO(LINUX_EGL_GET_DISPLAY_FAILED, "eglGetDisplay() failed") \
  1449. _SAPP_LOGITEM_XMACRO(LINUX_EGL_INITIALIZE_FAILED, "eglInitialize() failed") \
  1450. _SAPP_LOGITEM_XMACRO(LINUX_EGL_NO_CONFIGS, "eglChooseConfig() returned no configs") \
  1451. _SAPP_LOGITEM_XMACRO(LINUX_EGL_NO_NATIVE_VISUAL, "eglGetConfigAttrib() for EGL_NATIVE_VISUAL_ID failed") \
  1452. _SAPP_LOGITEM_XMACRO(LINUX_EGL_GET_VISUAL_INFO_FAILED, "XGetVisualInfo() failed") \
  1453. _SAPP_LOGITEM_XMACRO(LINUX_EGL_CREATE_WINDOW_SURFACE_FAILED, "eglCreateWindowSurface() failed") \
  1454. _SAPP_LOGITEM_XMACRO(LINUX_EGL_CREATE_CONTEXT_FAILED, "eglCreateContext() failed") \
  1455. _SAPP_LOGITEM_XMACRO(LINUX_EGL_MAKE_CURRENT_FAILED, "eglMakeCurrent() failed") \
  1456. _SAPP_LOGITEM_XMACRO(LINUX_X11_OPEN_DISPLAY_FAILED, "XOpenDisplay() failed") \
  1457. _SAPP_LOGITEM_XMACRO(LINUX_X11_QUERY_SYSTEM_DPI_FAILED, "failed to query system dpi value, assuming default 96.0") \
  1458. _SAPP_LOGITEM_XMACRO(LINUX_X11_DROPPED_FILE_URI_WRONG_SCHEME, "dropped file URL doesn't start with 'file://'") \
  1459. _SAPP_LOGITEM_XMACRO(LINUX_X11_FAILED_TO_BECOME_OWNER_OF_CLIPBOARD, "X11: Failed to become owner of clipboard selection") \
  1460. _SAPP_LOGITEM_XMACRO(ANDROID_UNSUPPORTED_INPUT_EVENT_INPUT_CB, "unsupported input event encountered in _sapp_android_input_cb()") \
  1461. _SAPP_LOGITEM_XMACRO(ANDROID_UNSUPPORTED_INPUT_EVENT_MAIN_CB, "unsupported input event encountered in _sapp_android_main_cb()") \
  1462. _SAPP_LOGITEM_XMACRO(ANDROID_READ_MSG_FAILED, "failed to read message in _sapp_android_main_cb()") \
  1463. _SAPP_LOGITEM_XMACRO(ANDROID_WRITE_MSG_FAILED, "failed to write message in _sapp_android_msg") \
  1464. _SAPP_LOGITEM_XMACRO(ANDROID_MSG_CREATE, "MSG_CREATE") \
  1465. _SAPP_LOGITEM_XMACRO(ANDROID_MSG_RESUME, "MSG_RESUME") \
  1466. _SAPP_LOGITEM_XMACRO(ANDROID_MSG_PAUSE, "MSG_PAUSE") \
  1467. _SAPP_LOGITEM_XMACRO(ANDROID_MSG_FOCUS, "MSG_FOCUS") \
  1468. _SAPP_LOGITEM_XMACRO(ANDROID_MSG_NO_FOCUS, "MSG_NO_FOCUS") \
  1469. _SAPP_LOGITEM_XMACRO(ANDROID_MSG_SET_NATIVE_WINDOW, "MSG_SET_NATIVE_WINDOW") \
  1470. _SAPP_LOGITEM_XMACRO(ANDROID_MSG_SET_INPUT_QUEUE, "MSG_SET_INPUT_QUEUE") \
  1471. _SAPP_LOGITEM_XMACRO(ANDROID_MSG_DESTROY, "MSG_DESTROY") \
  1472. _SAPP_LOGITEM_XMACRO(ANDROID_UNKNOWN_MSG, "unknown msg type received") \
  1473. _SAPP_LOGITEM_XMACRO(ANDROID_LOOP_THREAD_STARTED, "loop thread started") \
  1474. _SAPP_LOGITEM_XMACRO(ANDROID_LOOP_THREAD_DONE, "loop thread done") \
  1475. _SAPP_LOGITEM_XMACRO(ANDROID_NATIVE_ACTIVITY_ONSTART, "NativeActivity onStart()") \
  1476. _SAPP_LOGITEM_XMACRO(ANDROID_NATIVE_ACTIVITY_ONRESUME, "NativeActivity onResume") \
  1477. _SAPP_LOGITEM_XMACRO(ANDROID_NATIVE_ACTIVITY_ONSAVEINSTANCESTATE, "NativeActivity onSaveInstanceState") \
  1478. _SAPP_LOGITEM_XMACRO(ANDROID_NATIVE_ACTIVITY_ONWINDOWFOCUSCHANGED, "NativeActivity onWindowFocusChanged") \
  1479. _SAPP_LOGITEM_XMACRO(ANDROID_NATIVE_ACTIVITY_ONPAUSE, "NativeActivity onPause") \
  1480. _SAPP_LOGITEM_XMACRO(ANDROID_NATIVE_ACTIVITY_ONSTOP, "NativeActivity onStop()") \
  1481. _SAPP_LOGITEM_XMACRO(ANDROID_NATIVE_ACTIVITY_ONNATIVEWINDOWCREATED, "NativeActivity onNativeWindowCreated") \
  1482. _SAPP_LOGITEM_XMACRO(ANDROID_NATIVE_ACTIVITY_ONNATIVEWINDOWDESTROYED, "NativeActivity onNativeWindowDestroyed") \
  1483. _SAPP_LOGITEM_XMACRO(ANDROID_NATIVE_ACTIVITY_ONINPUTQUEUECREATED, "NativeActivity onInputQueueCreated") \
  1484. _SAPP_LOGITEM_XMACRO(ANDROID_NATIVE_ACTIVITY_ONINPUTQUEUEDESTROYED, "NativeActivity onInputQueueDestroyed") \
  1485. _SAPP_LOGITEM_XMACRO(ANDROID_NATIVE_ACTIVITY_ONCONFIGURATIONCHANGED, "NativeActivity onConfigurationChanged") \
  1486. _SAPP_LOGITEM_XMACRO(ANDROID_NATIVE_ACTIVITY_ONLOWMEMORY, "NativeActivity onLowMemory") \
  1487. _SAPP_LOGITEM_XMACRO(ANDROID_NATIVE_ACTIVITY_ONDESTROY, "NativeActivity onDestroy") \
  1488. _SAPP_LOGITEM_XMACRO(ANDROID_NATIVE_ACTIVITY_DONE, "NativeActivity done") \
  1489. _SAPP_LOGITEM_XMACRO(ANDROID_NATIVE_ACTIVITY_ONCREATE, "NativeActivity onCreate") \
  1490. _SAPP_LOGITEM_XMACRO(ANDROID_CREATE_THREAD_PIPE_FAILED, "failed to create thread pipe") \
  1491. _SAPP_LOGITEM_XMACRO(ANDROID_NATIVE_ACTIVITY_CREATE_SUCCESS, "NativeActivity successfully created") \
  1492. _SAPP_LOGITEM_XMACRO(WGPU_DEVICE_LOST, "wgpu: device lost") \
  1493. _SAPP_LOGITEM_XMACRO(WGPU_DEVICE_LOG, "wgpu: device log") \
  1494. _SAPP_LOGITEM_XMACRO(WGPU_DEVICE_UNCAPTURED_ERROR, "wgpu: uncaptured error") \
  1495. _SAPP_LOGITEM_XMACRO(WGPU_SWAPCHAIN_CREATE_SURFACE_FAILED, "wgpu: failed to create surface for swapchain") \
  1496. _SAPP_LOGITEM_XMACRO(WGPU_SWAPCHAIN_SURFACE_GET_CAPABILITIES_FAILED, "wgpu: wgpuSurfaceGetCapabilities failed") \
  1497. _SAPP_LOGITEM_XMACRO(WGPU_SWAPCHAIN_CREATE_DEPTH_STENCIL_TEXTURE_FAILED, "wgpu: failed to create depth-stencil texture for swapchain") \
  1498. _SAPP_LOGITEM_XMACRO(WGPU_SWAPCHAIN_CREATE_DEPTH_STENCIL_VIEW_FAILED, "wgpu: failed to create view object for swapchain depth-stencil texture") \
  1499. _SAPP_LOGITEM_XMACRO(WGPU_SWAPCHAIN_CREATE_MSAA_TEXTURE_FAILED, "wgpu: failed to create msaa texture for swapchain") \
  1500. _SAPP_LOGITEM_XMACRO(WGPU_SWAPCHAIN_CREATE_MSAA_VIEW_FAILED, "wgpu: failed to create view object for swapchain msaa texture") \
  1501. _SAPP_LOGITEM_XMACRO(WGPU_SWAPCHAIN_GETCURRENTTEXTURE_FAILED, "wgpu: wgpuSurfaceGetCurrentTexture() failed") \
  1502. _SAPP_LOGITEM_XMACRO(WGPU_REQUEST_DEVICE_STATUS_ERROR, "wgpu: requesting device failed with status 'error'") \
  1503. _SAPP_LOGITEM_XMACRO(WGPU_REQUEST_DEVICE_STATUS_UNKNOWN, "wgpu: requesting device failed with status 'unknown'") \
  1504. _SAPP_LOGITEM_XMACRO(WGPU_REQUEST_ADAPTER_STATUS_UNAVAILABLE, "wgpu: requesting adapter failed with 'unavailable'") \
  1505. _SAPP_LOGITEM_XMACRO(WGPU_REQUEST_ADAPTER_STATUS_ERROR, "wgpu: requesting adapter failed with status 'error'") \
  1506. _SAPP_LOGITEM_XMACRO(WGPU_REQUEST_ADAPTER_STATUS_UNKNOWN, "wgpu: requesting adapter failed with status 'unknown'") \
  1507. _SAPP_LOGITEM_XMACRO(WGPU_CREATE_INSTANCE_FAILED, "wgpu: failed to create instance") \
  1508. _SAPP_LOGITEM_XMACRO(VULKAN_ALLOC_DEVICE_MEMORY_NO_SUITABLE_MEMORY_TYPE, "vulkan: could not find suitable memory type") \
  1509. _SAPP_LOGITEM_XMACRO(VULKAN_ALLOCATE_MEMORY_FAILED, "vulkan: vkAllocateMemory() failed!") \
  1510. _SAPP_LOGITEM_XMACRO(VULKAN_CREATE_INSTANCE_FAILED, "vulkan: vkCreateInstance failed") \
  1511. _SAPP_LOGITEM_XMACRO(VULKAN_ENUMERATE_PHYSICAL_DEVICES_FAILED, "vulkan: vkEnumeratePhysicalDevices failed") \
  1512. _SAPP_LOGITEM_XMACRO(VULKAN_NO_PHYSICAL_DEVICES_FOUND, "vulkan: vkEnumeratePhysicalDevices return no devices") \
  1513. _SAPP_LOGITEM_XMACRO(VULKAN_NO_SUITABLE_PHYSICAL_DEVICE_FOUND, "vulkan: no suitable physical device found") \
  1514. _SAPP_LOGITEM_XMACRO(VULKAN_CREATE_DEVICE_FAILED_EXTENSION_NOT_PRESENT, "vulkan: vkCreateDevice failed (extension not present)") \
  1515. _SAPP_LOGITEM_XMACRO(VULKAN_CREATE_DEVICE_FAILED_FEATURE_NOT_PRESENT, "vulkan: vkCreateDevice failed (feature not present)") \
  1516. _SAPP_LOGITEM_XMACRO(VULKAN_CREATE_DEVICE_FAILED_INITIALIZATION_FAILED, "vulkan: vkCreateDevice failed (initialization failed)") \
  1517. _SAPP_LOGITEM_XMACRO(VULKAN_CREATE_DEVICE_FAILED_OTHER, "vulkan: vkCreateDevice failed (other)") \
  1518. _SAPP_LOGITEM_XMACRO(VULKAN_CREATE_SURFACE_FAILED, "vulkan: vkCreate*SurfaceKHR failed") \
  1519. _SAPP_LOGITEM_XMACRO(VULKAN_CREATE_SWAPCHAIN_FAILED, "vulkan: vkCreateSwapchainKHR failed") \
  1520. _SAPP_LOGITEM_XMACRO(VULKAN_SWAPCHAIN_CREATE_IMAGE_VIEW_FAILED, "vulkan: vkCreateImageView for swapchain image failed") \
  1521. _SAPP_LOGITEM_XMACRO(VULKAN_SWAPCHAIN_CREATE_IMAGE_FAILED, "vulkan: vkCreateImage for depth-stencil image failed") \
  1522. _SAPP_LOGITEM_XMACRO(VULKAN_SWAPCHAIN_ALLOC_IMAGE_DEVICE_MEMORY_FAILED, "vulkan: failed to allocate device memory for depth-stencil image") \
  1523. _SAPP_LOGITEM_XMACRO(VULKAN_SWAPCHAIN_BIND_IMAGE_MEMORY_FAILED, "vulkan: vkBindImageMemory() for depth-stencil image failed") \
  1524. _SAPP_LOGITEM_XMACRO(VULKAN_ACQUIRE_NEXT_IMAGE_FAILED, "vulkan: vkAcquireNextImageKHR failed") \
  1525. _SAPP_LOGITEM_XMACRO(VULKAN_QUEUE_PRESENT_FAILED, "vulkan: vkQueuePresentKHR failed") \
  1526. _SAPP_LOGITEM_XMACRO(IMAGE_DATA_SIZE_MISMATCH, "image data size mismatch (must be width*height*4 bytes)") \
  1527. _SAPP_LOGITEM_XMACRO(DROPPED_FILE_PATH_TOO_LONG, "dropped file path too long (sapp_desc.max_dropped_filed_path_length)") \
  1528. _SAPP_LOGITEM_XMACRO(CLIPBOARD_STRING_TOO_BIG, "clipboard string didn't fit into clipboard buffer") \
  1529. #define _SAPP_LOGITEM_XMACRO(item,msg) SAPP_LOGITEM_##item,
  1530. typedef enum sapp_log_item {
  1531. _SAPP_LOG_ITEMS
  1532. } sapp_log_item;
  1533. #undef _SAPP_LOGITEM_XMACRO
  1534. /*
  1535. sapp_pixel_format
  1536. Defines the pixel format for swapchain surfaces.
  1537. NOTE: when using sokol_gfx.h do not assume that the underlying
  1538. values are compatible with sg_pixel_format!
  1539. */
  1540. typedef enum sapp_pixel_format {
  1541. _SAPP_PIXELFORMAT_DEFAULT,
  1542. SAPP_PIXELFORMAT_NONE,
  1543. SAPP_PIXELFORMAT_RGBA8,
  1544. SAPP_PIXELFORMAT_SRGB8A8,
  1545. SAPP_PIXELFORMAT_BGRA8,
  1546. SAPP_PIXELFORMAT_SBGRA8,
  1547. SAPP_PIXELFORMAT_DEPTH,
  1548. SAPP_PIXELFORMAT_DEPTH_STENCIL,
  1549. _SA_PPPIXELFORMAT_FORCE_U32 = 0x7FFFFFFF
  1550. } sapp_pixel_format;
  1551. /*
  1552. sapp_environment
  1553. Used to provide runtime environment information to the
  1554. outside world (like default pixel formats and the backend
  1555. 3D API device pointer) via a call to sapp_get_environment().
  1556. NOTE: when using sokol_gfx.h, don't assume that sapp_environment
  1557. is binary compatible with sg_environment! Always use a translation
  1558. function like sglue_environment() to populate sg_environment
  1559. from sapp_environment!
  1560. */
  1561. typedef struct sapp_environment_defaults {
  1562. sapp_pixel_format color_format;
  1563. sapp_pixel_format depth_format;
  1564. int sample_count;
  1565. } sapp_environment_defaults;
  1566. typedef struct sapp_metal_environment {
  1567. const void* device;
  1568. } sapp_metal_environment;
  1569. typedef struct sapp_d3d11_environment {
  1570. const void* device;
  1571. const void* device_context;
  1572. } sapp_d3d11_environment;
  1573. typedef struct sapp_wgpu_environment {
  1574. const void* device;
  1575. } sapp_wgpu_environment;
  1576. typedef struct sapp_vulkan_environment {
  1577. const void* physical_device;
  1578. const void* device;
  1579. const void* queue;
  1580. uint32_t queue_family_index;
  1581. } sapp_vulkan_environment;
  1582. typedef struct sapp_environment {
  1583. sapp_environment_defaults defaults;
  1584. sapp_metal_environment metal;
  1585. sapp_d3d11_environment d3d11;
  1586. sapp_wgpu_environment wgpu;
  1587. sapp_vulkan_environment vulkan;
  1588. } sapp_environment;
  1589. /*
  1590. sapp_swapchain
  1591. Provides swapchain information for the current frame to the outside
  1592. world via a call to sapp_get_swapchain().
  1593. NOTE: sapp_get_swapchain() must be called exactly once per frame since
  1594. on some backends it will also acquire the next swapchain image.
  1595. NOTE: when using sokol_gfx.h, don't assume that the sapp_swapchain struct
  1596. has the same memory layout as sg_swapchain! Use the sokol_log.h helper
  1597. function sglue_swapchain() to translate sapp_swapchain into a
  1598. sg_swapchain instead.
  1599. */
  1600. typedef struct sapp_metal_swapchain {
  1601. const void* current_drawable; // CAMetalDrawable (NOT MTLDrawable!!!)
  1602. const void* depth_stencil_texture; // MTLTexture
  1603. const void* msaa_color_texture; // MTLTexture
  1604. } sapp_metal_swapchain;
  1605. typedef struct sapp_d3d11_swapchain {
  1606. const void* render_view; // ID3D11RenderTargetView
  1607. const void* resolve_view; // ID3D11RenderTargetView
  1608. const void* depth_stencil_view; // ID3D11DepthStencilView
  1609. } sapp_d3d11_swapchain;
  1610. typedef struct sapp_wgpu_swapchain {
  1611. const void* render_view; // WGPUTextureView
  1612. const void* resolve_view; // WGPUTextureView
  1613. const void* depth_stencil_view; // WGPUTextureView
  1614. } sapp_wgpu_swapchain;
  1615. typedef struct sapp_vulkan_swapchain {
  1616. const void* render_image; // vkImage
  1617. const void* render_view; // vkImageView
  1618. const void* resolve_image; // vkImage;
  1619. const void* resolve_view; // vkImageView
  1620. const void* depth_stencil_image; // vkImage
  1621. const void* depth_stencil_view; // vkImageView
  1622. const void* render_finished_semaphore; // vkSemaphore
  1623. const void* present_complete_semaphore; // vkSemaphore
  1624. } sapp_vulkan_swapchain;
  1625. typedef struct sapp_gl_swapchain {
  1626. uint32_t framebuffer; // GL framebuffer object
  1627. } sapp_gl_swapchain;
  1628. typedef struct sapp_swapchain {
  1629. int width;
  1630. int height;
  1631. int sample_count;
  1632. sapp_pixel_format color_format;
  1633. sapp_pixel_format depth_format;
  1634. sapp_metal_swapchain metal;
  1635. sapp_d3d11_swapchain d3d11;
  1636. sapp_wgpu_swapchain wgpu;
  1637. sapp_vulkan_swapchain vulkan;
  1638. sapp_gl_swapchain gl;
  1639. } sapp_swapchain;
  1640. /*
  1641. sapp_logger
  1642. Used in sapp_desc to provide a logging function. Please be aware that
  1643. without logging function, sokol-app will be completely silent, e.g. it will
  1644. not report errors or warnings. For maximum error verbosity, compile in
  1645. debug mode (e.g. NDEBUG *not* defined) and install a logger (for instance
  1646. the standard logging function from sokol_log.h).
  1647. */
  1648. typedef struct sapp_logger {
  1649. void (*func)(
  1650. const char* tag, // always "sapp"
  1651. uint32_t log_level, // 0=panic, 1=error, 2=warning, 3=info
  1652. uint32_t log_item_id, // SAPP_LOGITEM_*
  1653. const char* message_or_null, // a message string, may be nullptr in release mode
  1654. uint32_t line_nr, // line number in sokol_app.h
  1655. const char* filename_or_null, // source filename, may be nullptr in release mode
  1656. void* user_data);
  1657. void* user_data;
  1658. } sapp_logger;
  1659. /*
  1660. sokol-app initialization options, used as return value of sokol_main()
  1661. or sapp_run() argument.
  1662. */
  1663. typedef struct sapp_gl_desc {
  1664. int major_version; // override GL/GLES major and minor version (defaults: GL4.1 (macOS) or GL4.3, GLES3.1 (Android) or GLES3.0
  1665. int minor_version;
  1666. } sapp_gl_desc;
  1667. typedef struct sapp_win32_desc {
  1668. bool console_utf8; // if true, set the output console codepage to UTF-8
  1669. bool console_create; // if true, attach stdout/stderr to a new console window
  1670. bool console_attach; // if true, attach stdout/stderr to parent process
  1671. } sapp_win32_desc;
  1672. typedef struct sapp_html5_desc {
  1673. const char* canvas_selector; // css selector of the HTML5 canvas element, default is "#canvas"
  1674. bool canvas_resize; // if true, the HTML5 canvas size is set to sapp_desc.width/height, otherwise canvas size is tracked
  1675. bool preserve_drawing_buffer; // HTML5 only: whether to preserve default framebuffer content between frames
  1676. bool premultiplied_alpha; // HTML5 only: whether the rendered pixels use premultiplied alpha convention
  1677. bool ask_leave_site; // initial state of the internal html5_ask_leave_site flag (see sapp_html5_ask_leave_site())
  1678. bool update_document_title; // if true, update the HTML document.title with sapp_desc.window_title
  1679. bool bubble_mouse_events; // if true, mouse events will bubble up to the web page
  1680. bool bubble_touch_events; // same for touch events
  1681. bool bubble_wheel_events; // same for wheel events
  1682. bool bubble_key_events; // if true, bubble up *all* key events to browser, not just key events that represent characters
  1683. bool bubble_char_events; // if true, bubble up character events to browser
  1684. bool use_emsc_set_main_loop; // if true, use emscripten_set_main_loop() instead of emscripten_request_animation_frame_loop()
  1685. bool emsc_set_main_loop_simulate_infinite_loop; // this will be passed as the simulate_infinite_loop arg to emscripten_set_main_loop()
  1686. } sapp_html5_desc;
  1687. typedef struct sapp_ios_desc {
  1688. bool keyboard_resizes_canvas; // if true, showing the iOS keyboard shrinks the canvas
  1689. } sapp_ios_desc;
  1690. typedef struct sapp_desc {
  1691. void (*init_cb)(void); // these are the user-provided callbacks without user data
  1692. void (*frame_cb)(void);
  1693. void (*cleanup_cb)(void);
  1694. void (*event_cb)(const sapp_event*);
  1695. void* user_data; // these are the user-provided callbacks with user data
  1696. void (*init_userdata_cb)(void*);
  1697. void (*frame_userdata_cb)(void*);
  1698. void (*cleanup_userdata_cb)(void*);
  1699. void (*event_userdata_cb)(const sapp_event*, void*);
  1700. int width; // the preferred width of the window / canvas
  1701. int height; // the preferred height of the window / canvas
  1702. int sample_count; // MSAA sample count
  1703. int swap_interval; // the preferred swap interval (ignored on some platforms)
  1704. bool high_dpi; // whether the rendering canvas is full-resolution on HighDPI displays
  1705. bool fullscreen; // whether the window should be created in fullscreen mode
  1706. bool alpha; // whether the framebuffer should have an alpha channel (ignored on some platforms)
  1707. const char* window_title; // the window title as UTF-8 encoded string
  1708. bool enable_clipboard; // enable clipboard access, default is false
  1709. int clipboard_size; // max size of clipboard content in bytes
  1710. bool enable_dragndrop; // enable file dropping (drag'n'drop), default is false
  1711. int max_dropped_files; // max number of dropped files to process (default: 1)
  1712. int max_dropped_file_path_length; // max length in bytes of a dropped UTF-8 file path (default: 2048)
  1713. sapp_icon_desc icon; // the initial window icon to set
  1714. sapp_allocator allocator; // optional memory allocation overrides (default: malloc/free)
  1715. sapp_logger logger; // logging callback override (default: NO LOGGING!)
  1716. // backend-specific options
  1717. sapp_gl_desc gl;
  1718. sapp_win32_desc win32;
  1719. sapp_html5_desc html5;
  1720. sapp_ios_desc ios;
  1721. } sapp_desc;
  1722. /* HTML5 specific: request and response structs for
  1723. asynchronously loading dropped-file content.
  1724. */
  1725. typedef enum sapp_html5_fetch_error {
  1726. SAPP_HTML5_FETCH_ERROR_NO_ERROR,
  1727. SAPP_HTML5_FETCH_ERROR_BUFFER_TOO_SMALL,
  1728. SAPP_HTML5_FETCH_ERROR_OTHER,
  1729. } sapp_html5_fetch_error;
  1730. typedef struct sapp_html5_fetch_response {
  1731. bool succeeded; // true if the loading operation has succeeded
  1732. sapp_html5_fetch_error error_code;
  1733. int file_index; // index of the dropped file (0..sapp_get_num_dropped_filed()-1)
  1734. sapp_range data; // pointer and size of the fetched data (data.ptr == buffer.ptr, data.size <= buffer.size)
  1735. sapp_range buffer; // the user-provided buffer ptr/size pair (buffer.ptr == data.ptr, buffer.size >= data.size)
  1736. void* user_data; // user-provided user data pointer
  1737. } sapp_html5_fetch_response;
  1738. typedef struct sapp_html5_fetch_request {
  1739. int dropped_file_index; // 0..sapp_get_num_dropped_files()-1
  1740. void (*callback)(const sapp_html5_fetch_response*); // response callback function pointer (required)
  1741. sapp_range buffer; // ptr/size of a memory buffer to load the data into
  1742. void* user_data; // optional userdata pointer
  1743. } sapp_html5_fetch_request;
  1744. /*
  1745. sapp_mouse_cursor
  1746. Predefined cursor image definitions, set with sapp_set_mouse_cursor(sapp_mouse_cursor cursor)
  1747. */
  1748. typedef enum sapp_mouse_cursor {
  1749. SAPP_MOUSECURSOR_DEFAULT = 0, // equivalent with system default cursor
  1750. SAPP_MOUSECURSOR_ARROW,
  1751. SAPP_MOUSECURSOR_IBEAM,
  1752. SAPP_MOUSECURSOR_CROSSHAIR,
  1753. SAPP_MOUSECURSOR_POINTING_HAND,
  1754. SAPP_MOUSECURSOR_RESIZE_EW,
  1755. SAPP_MOUSECURSOR_RESIZE_NS,
  1756. SAPP_MOUSECURSOR_RESIZE_NWSE,
  1757. SAPP_MOUSECURSOR_RESIZE_NESW,
  1758. SAPP_MOUSECURSOR_RESIZE_ALL,
  1759. SAPP_MOUSECURSOR_NOT_ALLOWED,
  1760. SAPP_MOUSECURSOR_CUSTOM_0,
  1761. SAPP_MOUSECURSOR_CUSTOM_1,
  1762. SAPP_MOUSECURSOR_CUSTOM_2,
  1763. SAPP_MOUSECURSOR_CUSTOM_3,
  1764. SAPP_MOUSECURSOR_CUSTOM_4,
  1765. SAPP_MOUSECURSOR_CUSTOM_5,
  1766. SAPP_MOUSECURSOR_CUSTOM_6,
  1767. SAPP_MOUSECURSOR_CUSTOM_7,
  1768. SAPP_MOUSECURSOR_CUSTOM_8,
  1769. SAPP_MOUSECURSOR_CUSTOM_9,
  1770. SAPP_MOUSECURSOR_CUSTOM_10,
  1771. SAPP_MOUSECURSOR_CUSTOM_11,
  1772. SAPP_MOUSECURSOR_CUSTOM_12,
  1773. SAPP_MOUSECURSOR_CUSTOM_13,
  1774. SAPP_MOUSECURSOR_CUSTOM_14,
  1775. SAPP_MOUSECURSOR_CUSTOM_15,
  1776. _SAPP_MOUSECURSOR_NUM,
  1777. } sapp_mouse_cursor;
  1778. /* user-provided functions */
  1779. extern sapp_desc sokol_main(int argc, char* argv[]);
  1780. /* returns true after sokol-app has been initialized */
  1781. SOKOL_APP_API_DECL bool sapp_isvalid(void);
  1782. /* returns the current framebuffer width in pixels */
  1783. SOKOL_APP_API_DECL int sapp_width(void);
  1784. /* same as sapp_width(), but returns float */
  1785. SOKOL_APP_API_DECL float sapp_widthf(void);
  1786. /* returns the current framebuffer height in pixels */
  1787. SOKOL_APP_API_DECL int sapp_height(void);
  1788. /* same as sapp_height(), but returns float */
  1789. SOKOL_APP_API_DECL float sapp_heightf(void);
  1790. /* get default framebuffer color pixel format */
  1791. SOKOL_APP_API_DECL sapp_pixel_format sapp_color_format(void);
  1792. /* get default framebuffer depth pixel format */
  1793. SOKOL_APP_API_DECL sapp_pixel_format sapp_depth_format(void);
  1794. /* get default framebuffer sample count */
  1795. SOKOL_APP_API_DECL int sapp_sample_count(void);
  1796. /* returns true when high_dpi was requested and actually running in a high-dpi scenario */
  1797. SOKOL_APP_API_DECL bool sapp_high_dpi(void);
  1798. /* returns the dpi scaling factor (window pixels to framebuffer pixels) */
  1799. SOKOL_APP_API_DECL float sapp_dpi_scale(void);
  1800. /* show or hide the mobile device onscreen keyboard */
  1801. SOKOL_APP_API_DECL void sapp_show_keyboard(bool show);
  1802. /* return true if the mobile device onscreen keyboard is currently shown */
  1803. SOKOL_APP_API_DECL bool sapp_keyboard_shown(void);
  1804. /* query fullscreen mode */
  1805. SOKOL_APP_API_DECL bool sapp_is_fullscreen(void);
  1806. /* toggle fullscreen mode */
  1807. SOKOL_APP_API_DECL void sapp_toggle_fullscreen(void);
  1808. /* show or hide the mouse cursor */
  1809. SOKOL_APP_API_DECL void sapp_show_mouse(bool show);
  1810. /* show or hide the mouse cursor */
  1811. SOKOL_APP_API_DECL bool sapp_mouse_shown(void);
  1812. /* enable/disable mouse-pointer-lock mode */
  1813. SOKOL_APP_API_DECL void sapp_lock_mouse(bool lock);
  1814. /* return true if in mouse-pointer-lock mode (this may toggle a few frames later) */
  1815. SOKOL_APP_API_DECL bool sapp_mouse_locked(void);
  1816. /* set mouse cursor type */
  1817. SOKOL_APP_API_DECL void sapp_set_mouse_cursor(sapp_mouse_cursor cursor);
  1818. /* get current mouse cursor type */
  1819. SOKOL_APP_API_DECL sapp_mouse_cursor sapp_get_mouse_cursor(void);
  1820. /* associate a custom mouse cursor image to a sapp_mouse_cursor enum entry */
  1821. SOKOL_APP_API_DECL sapp_mouse_cursor sapp_bind_mouse_cursor_image(sapp_mouse_cursor cursor, const sapp_image_desc* desc);
  1822. /* restore the sapp_mouse_cursor enum entry to it's default system appearance */
  1823. SOKOL_APP_API_DECL void sapp_unbind_mouse_cursor_image(sapp_mouse_cursor cursor);
  1824. /* return the userdata pointer optionally provided in sapp_desc */
  1825. SOKOL_APP_API_DECL void* sapp_userdata(void);
  1826. /* return a copy of the sapp_desc structure */
  1827. SOKOL_APP_API_DECL sapp_desc sapp_query_desc(void);
  1828. /* initiate a "soft quit" (sends SAPP_EVENTTYPE_QUIT_REQUESTED) */
  1829. SOKOL_APP_API_DECL void sapp_request_quit(void);
  1830. /* cancel a pending quit (when SAPP_EVENTTYPE_QUIT_REQUESTED has been received) */
  1831. SOKOL_APP_API_DECL void sapp_cancel_quit(void);
  1832. /* initiate a "hard quit" (quit application without sending SAPP_EVENTTYPE_QUIT_REQUESTED) */
  1833. SOKOL_APP_API_DECL void sapp_quit(void);
  1834. /* call from inside event callback to consume the current event (don't forward to platform) */
  1835. SOKOL_APP_API_DECL void sapp_consume_event(void);
  1836. /* get the current frame counter (for comparison with sapp_event.frame_count) */
  1837. SOKOL_APP_API_DECL uint64_t sapp_frame_count(void);
  1838. /* get an averaged/smoothed frame duration in seconds */
  1839. SOKOL_APP_API_DECL double sapp_frame_duration(void);
  1840. /* write string into clipboard */
  1841. SOKOL_APP_API_DECL void sapp_set_clipboard_string(const char* str);
  1842. /* read string from clipboard (usually during SAPP_EVENTTYPE_CLIPBOARD_PASTED) */
  1843. SOKOL_APP_API_DECL const char* sapp_get_clipboard_string(void);
  1844. /* set the window title (only on desktop platforms) */
  1845. SOKOL_APP_API_DECL void sapp_set_window_title(const char* str);
  1846. /* set the window icon (only on Windows and Linux) */
  1847. SOKOL_APP_API_DECL void sapp_set_icon(const sapp_icon_desc* icon_desc);
  1848. /* gets the total number of dropped files (after an SAPP_EVENTTYPE_FILES_DROPPED event) */
  1849. SOKOL_APP_API_DECL int sapp_get_num_dropped_files(void);
  1850. /* gets the dropped file paths */
  1851. SOKOL_APP_API_DECL const char* sapp_get_dropped_file_path(int index);
  1852. /* special run-function for SOKOL_NO_ENTRY (in standard mode this is an empty stub) */
  1853. SOKOL_APP_API_DECL void sapp_run(const sapp_desc* desc);
  1854. /* get runtime environment information */
  1855. sapp_environment sapp_get_environment(void);
  1856. /* get current frame's swapchain information (call once per frame!) */
  1857. sapp_swapchain sapp_get_swapchain(void);
  1858. /* EGL: get EGLDisplay object */
  1859. SOKOL_APP_API_DECL const void* sapp_egl_get_display(void);
  1860. /* EGL: get EGLContext object */
  1861. SOKOL_APP_API_DECL const void* sapp_egl_get_context(void);
  1862. /* HTML5: enable or disable the hardwired "Leave Site?" dialog box */
  1863. SOKOL_APP_API_DECL void sapp_html5_ask_leave_site(bool ask);
  1864. /* HTML5: get byte size of a dropped file */
  1865. SOKOL_APP_API_DECL uint32_t sapp_html5_get_dropped_file_size(int index);
  1866. /* HTML5: asynchronously load the content of a dropped file */
  1867. SOKOL_APP_API_DECL void sapp_html5_fetch_dropped_file(const sapp_html5_fetch_request* request);
  1868. /* macOS: get bridged pointer to macOS NSWindow */
  1869. SOKOL_APP_API_DECL const void* sapp_macos_get_window(void);
  1870. /* iOS: get bridged pointer to iOS UIWindow */
  1871. SOKOL_APP_API_DECL const void* sapp_ios_get_window(void);
  1872. /* D3D11: get pointer to IDXGISwapChain object */
  1873. SOKOL_APP_API_DECL const void* sapp_d3d11_get_swap_chain(void);
  1874. /* Win32: get the HWND window handle */
  1875. SOKOL_APP_API_DECL const void* sapp_win32_get_hwnd(void);
  1876. /* GL: get major version */
  1877. SOKOL_APP_API_DECL int sapp_gl_get_major_version(void);
  1878. /* GL: get minor version */
  1879. SOKOL_APP_API_DECL int sapp_gl_get_minor_version(void);
  1880. /* GL: return true if the context is GLES */
  1881. SOKOL_APP_API_DECL bool sapp_gl_is_gles(void);
  1882. /* X11: get Window */
  1883. SOKOL_APP_API_DECL const void* sapp_x11_get_window(void);
  1884. /* X11: get Display */
  1885. SOKOL_APP_API_DECL const void* sapp_x11_get_display(void);
  1886. /* Android: get native activity handle */
  1887. SOKOL_APP_API_DECL const void* sapp_android_get_native_activity(void);
  1888. #ifdef __cplusplus
  1889. } /* extern "C" */
  1890. /* reference-based equivalents for C++ */
  1891. inline void sapp_run(const sapp_desc& desc) { return sapp_run(&desc); }
  1892. #endif
  1893. #endif // SOKOL_APP_INCLUDED
  1894. // ██ ███ ███ ██████ ██ ███████ ███ ███ ███████ ███ ██ ████████ █████ ████████ ██ ██████ ███ ██
  1895. // ██ ████ ████ ██ ██ ██ ██ ████ ████ ██ ████ ██ ██ ██ ██ ██ ██ ██ ██ ████ ██
  1896. // ██ ██ ████ ██ ██████ ██ █████ ██ ████ ██ █████ ██ ██ ██ ██ ███████ ██ ██ ██ ██ ██ ██ ██
  1897. // ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
  1898. // ██ ██ ██ ██ ███████ ███████ ██ ██ ███████ ██ ████ ██ ██ ██ ██ ██ ██████ ██ ████
  1899. //
  1900. // >>implementation
  1901. #ifdef SOKOL_APP_IMPL
  1902. #define SOKOL_APP_IMPL_INCLUDED (1)
  1903. #if defined(SOKOL_MALLOC) || defined(SOKOL_CALLOC) || defined(SOKOL_FREE)
  1904. #error "SOKOL_MALLOC/CALLOC/FREE macros are no longer supported, please use sapp_desc.allocator to override memory allocation functions"
  1905. #endif
  1906. #include <stdlib.h> // malloc, free
  1907. #include <string.h> // memset, strncmp
  1908. #include <stddef.h> // size_t
  1909. #include <math.h> // roundf
  1910. // helper macros
  1911. #define _sapp_def(val, def) (((val) == 0) ? (def) : (val))
  1912. #define _sapp_absf(a) (((a)<0.0f)?-(a):(a))
  1913. #ifdef __cplusplus
  1914. #define _SAPP_STRUCT(TYPE, NAME) TYPE NAME = {}
  1915. #else
  1916. #define _SAPP_STRUCT(TYPE, NAME) TYPE NAME = {0}
  1917. #endif
  1918. #define _SAPP_MAX_TITLE_LENGTH (128)
  1919. #define _SAPP_FALLBACK_DEFAULT_WINDOW_WIDTH (640)
  1920. #define _SAPP_FALLBACK_DEFAULT_WINDOW_HEIGHT (480)
  1921. // check if the config defines are alright
  1922. #if defined(__APPLE__)
  1923. // see https://clang.llvm.org/docs/LanguageExtensions.html#automatic-reference-counting
  1924. #if !defined(__cplusplus)
  1925. #if __has_feature(objc_arc) && !__has_feature(objc_arc_fields)
  1926. #error "sokol_app.h requires __has_feature(objc_arc_field) if ARC is enabled (use a more recent compiler version)"
  1927. #endif
  1928. #endif
  1929. #define _SAPP_APPLE (1)
  1930. #include <TargetConditionals.h>
  1931. #if defined(TARGET_OS_IPHONE) && !TARGET_OS_IPHONE
  1932. // MacOS
  1933. #define _SAPP_MACOS (1)
  1934. #if !defined(SOKOL_METAL) && !defined(SOKOL_GLCORE) && !defined(SOKOL_WGPU)
  1935. #error("sokol_app.h: unknown 3D API selected for MacOS, must be SOKOL_METAL, SOKOL_GLCORE or SOKOL_WGPU")
  1936. #endif
  1937. #else
  1938. // iOS or iOS Simulator
  1939. #define _SAPP_IOS (1)
  1940. #if !defined(SOKOL_METAL) && !defined(SOKOL_GLES3)
  1941. #error("sokol_app.h: unknown 3D API selected for iOS, must be SOKOL_METAL or SOKOL_GLES3")
  1942. #endif
  1943. #if TARGET_OS_TV
  1944. #define _SAPP_TVOS (1)
  1945. #endif
  1946. #endif
  1947. #elif defined(__EMSCRIPTEN__)
  1948. // Emscripten
  1949. #define _SAPP_EMSCRIPTEN (1)
  1950. #if !defined(SOKOL_GLES3) && !defined(SOKOL_WGPU)
  1951. #error("sokol_app.h: unknown 3D API selected for emscripten, must be SOKOL_GLES3 or SOKOL_WGPU")
  1952. #endif
  1953. #elif defined(_WIN32)
  1954. // Windows (D3D11 or GL)
  1955. #define _SAPP_WIN32 (1)
  1956. #if !defined(SOKOL_D3D11) && !defined(SOKOL_GLCORE) && !defined(SOKOL_WGPU) && !defined(SOKOL_NOAPI)
  1957. #error("sokol_app.h: unknown 3D API selected for Win32, must be SOKOL_D3D11, SOKOL_GLCORE, SOKOL_WGPU or SOKOL_NOAPI")
  1958. #endif
  1959. #elif defined(__ANDROID__)
  1960. // Android
  1961. #define _SAPP_ANDROID (1)
  1962. #if !defined(SOKOL_GLES3)
  1963. #error("sokol_app.h: unknown 3D API selected for Android, must be SOKOL_GLES3")
  1964. #endif
  1965. #if defined(SOKOL_NO_ENTRY)
  1966. #error("sokol_app.h: SOKOL_NO_ENTRY is not supported on Android")
  1967. #endif
  1968. #elif defined(__linux__) || defined(__unix__)
  1969. // Linux
  1970. #define _SAPP_LINUX (1)
  1971. #if !defined(SOKOL_GLCORE) && !defined(SOKOL_GLES3) && !defined(SOKOL_WGPU) && !defined(SOKOL_VULKAN)
  1972. #error("sokol_app.h: unknown 3D API selected for Linux, must be SOKOL_GLCORE, SOKOL_GLES3, SOKOL_WGPU or SOKOL_VULKAN")
  1973. #endif
  1974. #if defined(SOKOL_GLCORE)
  1975. #if defined(SOKOL_FORCE_EGL)
  1976. #define _SAPP_EGL (1)
  1977. #else
  1978. #define _SAPP_GLX (1)
  1979. #endif
  1980. #define GL_GLEXT_PROTOTYPES
  1981. #include <GL/gl.h>
  1982. #elif defined(SOKOL_GLES3)
  1983. #define _SAPP_EGL (1)
  1984. #include <GLES3/gl3.h>
  1985. #include <GLES3/gl3ext.h>
  1986. #elif defined(SOKOL_VULKAN)
  1987. #define VK_USE_PLATFORM_XLIB_KHR
  1988. #include <vulkan/vulkan.h>
  1989. #endif
  1990. #else
  1991. #error "sokol_app.h: Unknown platform"
  1992. #endif
  1993. #if defined(SOKOL_GLCORE) || defined(SOKOL_GLES3)
  1994. #define _SAPP_ANY_GL (1)
  1995. #endif
  1996. #ifndef SOKOL_API_IMPL
  1997. #define SOKOL_API_IMPL
  1998. #endif
  1999. #ifndef SOKOL_DEBUG
  2000. #ifndef NDEBUG
  2001. #define SOKOL_DEBUG
  2002. #endif
  2003. #endif
  2004. #ifndef SOKOL_ASSERT
  2005. #include <assert.h>
  2006. #define SOKOL_ASSERT(c) assert(c)
  2007. #endif
  2008. #ifndef SOKOL_UNREACHABLE
  2009. #define SOKOL_UNREACHABLE SOKOL_ASSERT(false)
  2010. #endif
  2011. #ifndef _SOKOL_PRIVATE
  2012. #if defined(__GNUC__) || defined(__clang__)
  2013. #define _SOKOL_PRIVATE __attribute__((unused)) static
  2014. #else
  2015. #define _SOKOL_PRIVATE static
  2016. #endif
  2017. #endif
  2018. #ifndef _SOKOL_UNUSED
  2019. #define _SOKOL_UNUSED(x) (void)(x)
  2020. #endif
  2021. #if defined(SOKOL_WGPU)
  2022. #include <webgpu/webgpu.h>
  2023. #if !defined(__EMSCRIPTEN__)
  2024. #define _SAPP_WGPU_HAS_WAIT (1)
  2025. #endif
  2026. #endif
  2027. #if defined(_SAPP_APPLE)
  2028. #ifndef GL_SILENCE_DEPRECATION
  2029. #define GL_SILENCE_DEPRECATION
  2030. #endif
  2031. #if defined(SOKOL_METAL)
  2032. #import <Metal/Metal.h>
  2033. #import <MetalKit/MetalKit.h>
  2034. #endif
  2035. #if defined(_SAPP_MACOS)
  2036. #import <Cocoa/Cocoa.h>
  2037. #if defined(_SAPP_ANY_GL)
  2038. #include <OpenGL/gl3.h>
  2039. #endif
  2040. #if defined(SOKOL_WGPU)
  2041. #import <QuartzCore/CAMetalLayer.h>
  2042. #import <QuartzCore/CADisplayLink.h>
  2043. #endif
  2044. #elif defined(_SAPP_IOS)
  2045. #import <UIKit/UIKit.h>
  2046. #if defined(_SAPP_ANY_GL)
  2047. #import <GLKit/GLKit.h>
  2048. #include <OpenGLES/ES3/gl.h>
  2049. #endif
  2050. #endif
  2051. #include <AvailabilityMacros.h>
  2052. #include <mach/mach_time.h>
  2053. #elif defined(_SAPP_EMSCRIPTEN)
  2054. #if defined(SOKOL_GLES3)
  2055. #include <GLES3/gl3.h>
  2056. #endif
  2057. #include <emscripten/emscripten.h>
  2058. #include <emscripten/html5.h>
  2059. #elif defined(_SAPP_WIN32)
  2060. #ifdef _MSC_VER
  2061. #pragma warning(push)
  2062. #pragma warning(disable:4201) /* nonstandard extension used: nameless struct/union */
  2063. #pragma warning(disable:4204) /* nonstandard extension used: non-constant aggregate initializer */
  2064. #pragma warning(disable:4054) /* 'type cast': from function pointer */
  2065. #pragma warning(disable:4055) /* 'type cast': from data pointer */
  2066. #pragma warning(disable:4505) /* unreferenced local function has been removed */
  2067. #pragma warning(disable:4115) /* /W4: 'ID3D11ModuleInstance': named type definition in parentheses (in d3d11.h) */
  2068. #endif
  2069. #ifndef WIN32_LEAN_AND_MEAN
  2070. #define WIN32_LEAN_AND_MEAN
  2071. #endif
  2072. #ifndef NOMINMAX
  2073. #define NOMINMAX
  2074. #endif
  2075. #include <windows.h>
  2076. #include <windowsx.h>
  2077. #include <shellapi.h>
  2078. #if defined(__GNUC__)
  2079. #pragma GCC diagnostic push
  2080. #pragma GCC diagnostic ignored "-Wunknown-pragmas"
  2081. #endif
  2082. #if !defined(SOKOL_NO_ENTRY) // if SOKOL_NO_ENTRY is defined, it's the application's responsibility to use the right subsystem
  2083. #if defined(SOKOL_WIN32_FORCE_MAIN) && defined(SOKOL_WIN32_FORCE_WINMAIN)
  2084. // If both are defined, it's the application's responsibility to use the right subsystem
  2085. #elif defined(SOKOL_WIN32_FORCE_MAIN)
  2086. #pragma comment (linker, "/subsystem:console")
  2087. #else
  2088. #pragma comment (linker, "/subsystem:windows")
  2089. #endif
  2090. #endif
  2091. #include <stdio.h> /* freopen_s() */
  2092. #include <wchar.h> /* wcslen() */
  2093. #pragma comment (lib, "kernel32")
  2094. #pragma comment (lib, "user32")
  2095. #pragma comment (lib, "shell32") /* CommandLineToArgvW, DragQueryFileW, DragFinished */
  2096. #pragma comment (lib, "gdi32")
  2097. #if defined(SOKOL_D3D11)
  2098. #pragma comment (lib, "dxgi")
  2099. #pragma comment (lib, "d3d11")
  2100. #endif
  2101. #if defined(__GNUC__)
  2102. #pragma GCC diagnostic pop
  2103. #endif
  2104. #if defined(SOKOL_D3D11)
  2105. #ifndef D3D11_NO_HELPERS
  2106. #define D3D11_NO_HELPERS
  2107. #endif
  2108. #include <d3d11.h>
  2109. #include <dxgi.h>
  2110. // DXGI_SWAP_EFFECT_FLIP_DISCARD is only defined in newer Windows SDKs, so don't depend on it
  2111. #define _SAPP_DXGI_SWAP_EFFECT_FLIP_DISCARD (4)
  2112. #endif
  2113. #ifndef WM_MOUSEHWHEEL /* see https://github.com/floooh/sokol/issues/138 */
  2114. #define WM_MOUSEHWHEEL (0x020E)
  2115. #endif
  2116. #ifndef WM_DPICHANGED
  2117. #define WM_DPICHANGED (0x02E0)
  2118. #endif
  2119. #elif defined(_SAPP_ANDROID)
  2120. #include <pthread.h>
  2121. #include <unistd.h>
  2122. #include <time.h>
  2123. #include <android/native_activity.h>
  2124. #include <android/looper.h>
  2125. #include <EGL/egl.h>
  2126. #include <GLES3/gl3.h>
  2127. #elif defined(_SAPP_LINUX)
  2128. #define GL_GLEXT_PROTOTYPES
  2129. #include <X11/Xlib.h>
  2130. #include <X11/Xutil.h>
  2131. #include <X11/XKBlib.h>
  2132. #include <X11/keysym.h>
  2133. #include <X11/Xresource.h>
  2134. #include <X11/Xatom.h>
  2135. #include <X11/extensions/XInput2.h>
  2136. #include <X11/Xcursor/Xcursor.h>
  2137. #include <X11/cursorfont.h> /* XC_* font cursors */
  2138. #include <X11/Xmd.h> /* CARD32 */
  2139. #if defined(_SAPP_EGL)
  2140. #include <EGL/egl.h>
  2141. #endif
  2142. #include <dlfcn.h> /* dlopen, dlsym, dlclose */
  2143. #include <limits.h> /* LONG_MAX */
  2144. #include <pthread.h> /* only used a linker-guard, search for _sapp_linux_run() and see first comment */
  2145. #include <time.h>
  2146. #include <poll.h>
  2147. #endif
  2148. #if defined(_SAPP_APPLE)
  2149. // this is ARC compatible
  2150. #if defined(__cplusplus)
  2151. #define _SAPP_CLEAR_ARC_STRUCT(type, item) { item = type(); }
  2152. #else
  2153. #define _SAPP_CLEAR_ARC_STRUCT(type, item) { item = (type) { 0 }; }
  2154. #endif
  2155. #else
  2156. #define _SAPP_CLEAR_ARC_STRUCT(type, item) { _sapp_clear(&item, sizeof(item)); }
  2157. #endif
  2158. // ███████ ██████ █████ ███ ███ ███████ ████████ ██ ███ ███ ██ ███ ██ ██████
  2159. // ██ ██ ██ ██ ██ ████ ████ ██ ██ ██ ████ ████ ██ ████ ██ ██
  2160. // █████ ██████ ███████ ██ ████ ██ █████ ██ ██ ██ ████ ██ ██ ██ ██ ██ ██ ███
  2161. // ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
  2162. // ██ ██ ██ ██ ██ ██ ██ ███████ ██ ██ ██ ██ ██ ██ ████ ██████
  2163. //
  2164. // >>frame timing
  2165. #define _SAPP_RING_NUM_SLOTS (256)
  2166. typedef struct {
  2167. int head;
  2168. int tail;
  2169. double buf[_SAPP_RING_NUM_SLOTS];
  2170. } _sapp_ring_t;
  2171. _SOKOL_PRIVATE int _sapp_ring_idx(int i) {
  2172. return i % _SAPP_RING_NUM_SLOTS;
  2173. }
  2174. _SOKOL_PRIVATE void _sapp_ring_init(_sapp_ring_t* ring) {
  2175. ring->head = 0;
  2176. ring->tail = 0;
  2177. }
  2178. _SOKOL_PRIVATE bool _sapp_ring_full(_sapp_ring_t* ring) {
  2179. return _sapp_ring_idx(ring->head + 1) == ring->tail;
  2180. }
  2181. _SOKOL_PRIVATE bool _sapp_ring_empty(_sapp_ring_t* ring) {
  2182. return ring->head == ring->tail;
  2183. }
  2184. _SOKOL_PRIVATE int _sapp_ring_count(_sapp_ring_t* ring) {
  2185. int count;
  2186. if (ring->head >= ring->tail) {
  2187. count = ring->head - ring->tail;
  2188. } else {
  2189. count = (ring->head + _SAPP_RING_NUM_SLOTS) - ring->tail;
  2190. }
  2191. SOKOL_ASSERT((count >= 0) && (count < _SAPP_RING_NUM_SLOTS));
  2192. return count;
  2193. }
  2194. _SOKOL_PRIVATE void _sapp_ring_enqueue(_sapp_ring_t* ring, double val) {
  2195. SOKOL_ASSERT(!_sapp_ring_full(ring));
  2196. ring->buf[ring->head] = val;
  2197. ring->head = _sapp_ring_idx(ring->head + 1);
  2198. }
  2199. _SOKOL_PRIVATE double _sapp_ring_dequeue(_sapp_ring_t* ring) {
  2200. SOKOL_ASSERT(!_sapp_ring_empty(ring));
  2201. double val = ring->buf[ring->tail];
  2202. ring->tail = _sapp_ring_idx(ring->tail + 1);
  2203. return val;
  2204. }
  2205. /*
  2206. NOTE:
  2207. Q: Why not use CAMetalDrawable.presentedTime on macOS and iOS?
  2208. A: The value appears to be highly unstable during the first few
  2209. seconds, sometimes several frames are dropped in sequence, or
  2210. switch between 120 and 60 Hz for a few frames. Simply measuring
  2211. and averaging the frame time yielded a more stable frame duration.
  2212. Maybe switching to CVDisplayLink would yield better results.
  2213. Until then just measure the time.
  2214. */
  2215. typedef struct {
  2216. #if defined(_SAPP_APPLE)
  2217. struct {
  2218. mach_timebase_info_data_t timebase;
  2219. uint64_t start;
  2220. } mach;
  2221. #elif defined(_SAPP_EMSCRIPTEN)
  2222. // empty
  2223. #elif defined(_SAPP_WIN32)
  2224. struct {
  2225. LARGE_INTEGER freq;
  2226. LARGE_INTEGER start;
  2227. } win;
  2228. #else // Linux, Android, ...
  2229. #ifdef CLOCK_MONOTONIC
  2230. #define _SAPP_CLOCK_MONOTONIC CLOCK_MONOTONIC
  2231. #else
  2232. // on some embedded platforms, CLOCK_MONOTONIC isn't defined
  2233. #define _SAPP_CLOCK_MONOTONIC (1)
  2234. #endif
  2235. struct {
  2236. uint64_t start;
  2237. } posix;
  2238. #endif
  2239. } _sapp_timestamp_t;
  2240. _SOKOL_PRIVATE int64_t _sapp_int64_muldiv(int64_t value, int64_t numer, int64_t denom) {
  2241. int64_t q = value / denom;
  2242. int64_t r = value % denom;
  2243. return q * numer + r * numer / denom;
  2244. }
  2245. _SOKOL_PRIVATE void _sapp_timestamp_init(_sapp_timestamp_t* ts) {
  2246. #if defined(_SAPP_APPLE)
  2247. mach_timebase_info(&ts->mach.timebase);
  2248. ts->mach.start = mach_absolute_time();
  2249. #elif defined(_SAPP_EMSCRIPTEN)
  2250. (void)ts;
  2251. #elif defined(_SAPP_WIN32)
  2252. QueryPerformanceFrequency(&ts->win.freq);
  2253. QueryPerformanceCounter(&ts->win.start);
  2254. #else
  2255. struct timespec tspec;
  2256. clock_gettime(_SAPP_CLOCK_MONOTONIC, &tspec);
  2257. ts->posix.start = (uint64_t)tspec.tv_sec*1000000000 + (uint64_t)tspec.tv_nsec;
  2258. #endif
  2259. }
  2260. _SOKOL_PRIVATE double _sapp_timestamp_now(_sapp_timestamp_t* ts) {
  2261. #if defined(_SAPP_APPLE)
  2262. const uint64_t traw = mach_absolute_time() - ts->mach.start;
  2263. const uint64_t now = (uint64_t) _sapp_int64_muldiv((int64_t)traw, (int64_t)ts->mach.timebase.numer, (int64_t)ts->mach.timebase.denom);
  2264. return (double)now / 1000000000.0;
  2265. #elif defined(_SAPP_EMSCRIPTEN)
  2266. (void)ts;
  2267. SOKOL_ASSERT(false);
  2268. return 0.0;
  2269. #elif defined(_SAPP_WIN32)
  2270. LARGE_INTEGER qpc;
  2271. QueryPerformanceCounter(&qpc);
  2272. const uint64_t now = (uint64_t)_sapp_int64_muldiv(qpc.QuadPart - ts->win.start.QuadPart, 1000000000, ts->win.freq.QuadPart);
  2273. return (double)now / 1000000000.0;
  2274. #else
  2275. struct timespec tspec;
  2276. clock_gettime(_SAPP_CLOCK_MONOTONIC, &tspec);
  2277. const uint64_t now = ((uint64_t)tspec.tv_sec*1000000000 + (uint64_t)tspec.tv_nsec) - ts->posix.start;
  2278. return (double)now / 1000000000.0;
  2279. #endif
  2280. }
  2281. typedef struct {
  2282. double last;
  2283. double accum;
  2284. double avg;
  2285. int spike_count;
  2286. int num;
  2287. _sapp_timestamp_t timestamp;
  2288. _sapp_ring_t ring;
  2289. } _sapp_timing_t;
  2290. _SOKOL_PRIVATE void _sapp_timing_reset(_sapp_timing_t* t) {
  2291. t->last = 0.0;
  2292. t->accum = 0.0;
  2293. t->spike_count = 0;
  2294. t->num = 0;
  2295. _sapp_ring_init(&t->ring);
  2296. }
  2297. _SOKOL_PRIVATE void _sapp_timing_init(_sapp_timing_t* t) {
  2298. t->avg = 1.0 / 60.0; // dummy value until first actual value is available
  2299. _sapp_timing_reset(t);
  2300. _sapp_timestamp_init(&t->timestamp);
  2301. }
  2302. _SOKOL_PRIVATE void _sapp_timing_put(_sapp_timing_t* t, double dur) {
  2303. // arbitrary upper limit to ignore outliers (e.g. during window resizing, or debugging)
  2304. double min_dur = 0.0;
  2305. double max_dur = 0.1;
  2306. // if we have enough samples for a useful average, use a much tighter 'valid window'
  2307. if (_sapp_ring_full(&t->ring)) {
  2308. min_dur = t->avg * 0.8;
  2309. max_dur = t->avg * 1.2;
  2310. }
  2311. if ((dur < min_dur) || (dur > max_dur)) {
  2312. t->spike_count++;
  2313. // if there have been many spikes in a row, the display refresh rate
  2314. // might have changed, so a timing reset is needed
  2315. if (t->spike_count > 20) {
  2316. _sapp_timing_reset(t);
  2317. }
  2318. return;
  2319. }
  2320. if (_sapp_ring_full(&t->ring)) {
  2321. double old_val = _sapp_ring_dequeue(&t->ring);
  2322. t->accum -= old_val;
  2323. t->num -= 1;
  2324. }
  2325. _sapp_ring_enqueue(&t->ring, dur);
  2326. t->accum += dur;
  2327. t->num += 1;
  2328. SOKOL_ASSERT(t->num > 0);
  2329. t->avg = t->accum / t->num;
  2330. t->spike_count = 0;
  2331. }
  2332. _SOKOL_PRIVATE void _sapp_timing_discontinuity(_sapp_timing_t* t) {
  2333. t->last = 0.0;
  2334. }
  2335. _SOKOL_PRIVATE void _sapp_timing_measure(_sapp_timing_t* t) {
  2336. const double now = _sapp_timestamp_now(&t->timestamp);
  2337. if (t->last > 0.0) {
  2338. double dur = now - t->last;
  2339. _sapp_timing_put(t, dur);
  2340. }
  2341. t->last = now;
  2342. }
  2343. _SOKOL_PRIVATE void _sapp_timing_external(_sapp_timing_t* t, double now) {
  2344. if (t->last > 0.0) {
  2345. double dur = now - t->last;
  2346. _sapp_timing_put(t, dur);
  2347. }
  2348. t->last = now;
  2349. }
  2350. _SOKOL_PRIVATE double _sapp_timing_get_avg(_sapp_timing_t* t) {
  2351. return t->avg;
  2352. }
  2353. // ███████ ████████ ██████ ██ ██ ██████ ████████ ███████
  2354. // ██ ██ ██ ██ ██ ██ ██ ██ ██
  2355. // ███████ ██ ██████ ██ ██ ██ ██ ███████
  2356. // ██ ██ ██ ██ ██ ██ ██ ██ ██
  2357. // ███████ ██ ██ ██ ██████ ██████ ██ ███████
  2358. //
  2359. // >> structs
  2360. #if defined(SOKOL_WGPU)
  2361. typedef struct {
  2362. WGPUInstance instance;
  2363. WGPUAdapter adapter;
  2364. WGPUDevice device;
  2365. WGPUSurface surface;
  2366. WGPUTextureFormat render_format;
  2367. WGPUTexture msaa_tex;
  2368. WGPUTextureView msaa_view;
  2369. WGPUTexture depth_stencil_tex;
  2370. WGPUTextureView depth_stencil_view;
  2371. WGPUTextureView swapchain_view;
  2372. bool init_done;
  2373. } _sapp_wgpu_t;
  2374. #endif
  2375. #if defined(SOKOL_VULKAN)
  2376. #define _SAPP_VK_MAX_SWAPCHAIN_IMAGES (8)
  2377. typedef struct {
  2378. VkImage img;
  2379. VkDeviceMemory mem;
  2380. VkImageView view;
  2381. } _sapp_vk_swapchain_surface_t;
  2382. typedef struct {
  2383. VkInstance instance;
  2384. VkSurfaceKHR surface;
  2385. VkSurfaceFormatKHR surface_format;
  2386. VkPhysicalDevice physical_device;
  2387. uint32_t queue_family_index;
  2388. VkDevice device;
  2389. VkQueue queue;
  2390. VkSwapchainKHR swapchain;
  2391. uint32_t num_swapchain_images;
  2392. uint32_t cur_swapchain_image_index;
  2393. VkImage swapchain_images[_SAPP_VK_MAX_SWAPCHAIN_IMAGES];
  2394. VkImageView swapchain_views[_SAPP_VK_MAX_SWAPCHAIN_IMAGES];
  2395. _sapp_vk_swapchain_surface_t msaa;
  2396. _sapp_vk_swapchain_surface_t depth;
  2397. uint32_t sync_slot;
  2398. struct {
  2399. VkSemaphore render_finished_sem;
  2400. VkSemaphore present_complete_sem;
  2401. } sync[_SAPP_VK_MAX_SWAPCHAIN_IMAGES];
  2402. } _sapp_vk_t;
  2403. #endif
  2404. #if defined(_SAPP_MACOS)
  2405. @interface _sapp_macos_app_delegate : NSObject<NSApplicationDelegate>
  2406. @end
  2407. @interface _sapp_macos_window : NSWindow
  2408. @end
  2409. @interface _sapp_macos_window_delegate : NSObject<NSWindowDelegate>
  2410. @end
  2411. #if defined(SOKOL_METAL)
  2412. @interface _sapp_macos_view : MTKView
  2413. @end
  2414. #elif defined(SOKOL_GLCORE)
  2415. @interface _sapp_macos_view : NSOpenGLView
  2416. - (void)timerFired:(id)sender;
  2417. @end
  2418. #elif defined(SOKOL_WGPU)
  2419. @interface _sapp_macos_view : NSView
  2420. - (void)displayLinkFired:(id)sender;
  2421. @end
  2422. #endif // SOKOL_GLCORE
  2423. typedef struct {
  2424. uint32_t flags_changed_store;
  2425. uint8_t mouse_buttons;
  2426. NSWindow* window;
  2427. NSTrackingArea* tracking_area;
  2428. id keyup_monitor;
  2429. _sapp_macos_app_delegate* app_dlg;
  2430. _sapp_macos_window_delegate* win_dlg;
  2431. _sapp_macos_view* view;
  2432. NSCursor* standard_cursors[_SAPP_MOUSECURSOR_NUM];
  2433. NSCursor* custom_cursors[_SAPP_MOUSECURSOR_NUM];
  2434. #if defined(SOKOL_METAL)
  2435. id<MTLDevice> mtl_device;
  2436. #endif
  2437. #if defined(SOKOL_WGPU)
  2438. struct {
  2439. CAMetalLayer* mtl_layer;
  2440. CADisplayLink* display_link;
  2441. } wgpu;
  2442. #endif
  2443. } _sapp_macos_t;
  2444. #endif // _SAPP_MACOS
  2445. #if defined(_SAPP_IOS)
  2446. @interface _sapp_app_delegate : NSObject<UIApplicationDelegate>
  2447. @end
  2448. @interface _sapp_textfield_dlg : NSObject<UITextFieldDelegate>
  2449. - (void)keyboardWasShown:(NSNotification*)notif;
  2450. - (void)keyboardWillBeHidden:(NSNotification*)notif;
  2451. - (void)keyboardDidChangeFrame:(NSNotification*)notif;
  2452. @end
  2453. #if defined(SOKOL_METAL)
  2454. @interface _sapp_ios_view : MTKView;
  2455. @end
  2456. #else
  2457. @interface _sapp_ios_view : GLKView
  2458. @end
  2459. #endif
  2460. typedef struct {
  2461. UIWindow* window;
  2462. _sapp_ios_view* view;
  2463. UITextField* textfield;
  2464. _sapp_textfield_dlg* textfield_dlg;
  2465. #if defined(SOKOL_METAL)
  2466. UIViewController* view_ctrl;
  2467. id<MTLDevice> mtl_device;
  2468. #else
  2469. GLKViewController* view_ctrl;
  2470. EAGLContext* eagl_ctx;
  2471. #endif
  2472. bool suspended;
  2473. } _sapp_ios_t;
  2474. #endif // _SAPP_IOS
  2475. #if defined(_SAPP_EMSCRIPTEN)
  2476. typedef struct {
  2477. bool mouse_lock_requested;
  2478. uint16_t mouse_buttons;
  2479. } _sapp_emsc_t;
  2480. #endif // _SAPP_EMSCRIPTEN
  2481. #if defined(SOKOL_D3D11) && defined(_SAPP_WIN32)
  2482. typedef struct {
  2483. ID3D11Device* device;
  2484. ID3D11DeviceContext* device_context;
  2485. ID3D11Texture2D* rt;
  2486. ID3D11RenderTargetView* rtv;
  2487. ID3D11Texture2D* msaa_rt;
  2488. ID3D11RenderTargetView* msaa_rtv;
  2489. ID3D11Texture2D* ds;
  2490. ID3D11DepthStencilView* dsv;
  2491. DXGI_SWAP_CHAIN_DESC swap_chain_desc;
  2492. IDXGISwapChain* swap_chain;
  2493. IDXGIDevice1* dxgi_device;
  2494. bool use_dxgi_frame_stats;
  2495. UINT sync_refresh_count;
  2496. } _sapp_d3d11_t;
  2497. #endif
  2498. #if defined(_SAPP_WIN32)
  2499. #ifndef DPI_ENUMS_DECLARED
  2500. typedef enum PROCESS_DPI_AWARENESS
  2501. {
  2502. PROCESS_DPI_UNAWARE = 0,
  2503. PROCESS_SYSTEM_DPI_AWARE = 1,
  2504. PROCESS_PER_MONITOR_DPI_AWARE = 2
  2505. } PROCESS_DPI_AWARENESS;
  2506. typedef enum MONITOR_DPI_TYPE {
  2507. MDT_EFFECTIVE_DPI = 0,
  2508. MDT_ANGULAR_DPI = 1,
  2509. MDT_RAW_DPI = 2,
  2510. MDT_DEFAULT = MDT_EFFECTIVE_DPI
  2511. } MONITOR_DPI_TYPE;
  2512. #endif // DPI_ENUMS_DECLARED
  2513. typedef struct {
  2514. bool aware;
  2515. float content_scale;
  2516. float window_scale;
  2517. float mouse_scale;
  2518. } _sapp_win32_dpi_t;
  2519. typedef struct {
  2520. HWND hwnd;
  2521. HMONITOR hmonitor;
  2522. HDC dc;
  2523. HICON big_icon;
  2524. HICON small_icon;
  2525. HCURSOR standard_cursors[_SAPP_MOUSECURSOR_NUM];
  2526. HCURSOR custom_cursors[_SAPP_MOUSECURSOR_NUM];
  2527. UINT orig_codepage;
  2528. WCHAR surrogate;
  2529. RECT stored_window_rect; // used to restore window pos/size when toggling fullscreen => windowed
  2530. bool is_win10_or_greater;
  2531. bool in_create_window;
  2532. bool iconified;
  2533. _sapp_win32_dpi_t dpi;
  2534. struct {
  2535. struct {
  2536. LONG pos_x, pos_y;
  2537. bool pos_valid;
  2538. } lock;
  2539. struct {
  2540. LONG pos_x, pos_y;
  2541. bool pos_valid;
  2542. } raw_input;
  2543. bool requested_lock;
  2544. bool tracked;
  2545. uint8_t capture_mask;
  2546. } mouse;
  2547. struct {
  2548. size_t size;
  2549. void* ptr;
  2550. } raw_input_data;
  2551. } _sapp_win32_t;
  2552. #if defined(SOKOL_GLCORE)
  2553. #define WGL_NUMBER_PIXEL_FORMATS_ARB 0x2000
  2554. #define WGL_SUPPORT_OPENGL_ARB 0x2010
  2555. #define WGL_DRAW_TO_WINDOW_ARB 0x2001
  2556. #define WGL_PIXEL_TYPE_ARB 0x2013
  2557. #define WGL_TYPE_RGBA_ARB 0x202b
  2558. #define WGL_ACCELERATION_ARB 0x2003
  2559. #define WGL_NO_ACCELERATION_ARB 0x2025
  2560. #define WGL_RED_BITS_ARB 0x2015
  2561. #define WGL_GREEN_BITS_ARB 0x2017
  2562. #define WGL_BLUE_BITS_ARB 0x2019
  2563. #define WGL_ALPHA_BITS_ARB 0x201b
  2564. #define WGL_DEPTH_BITS_ARB 0x2022
  2565. #define WGL_STENCIL_BITS_ARB 0x2023
  2566. #define WGL_DOUBLE_BUFFER_ARB 0x2011
  2567. #define WGL_SAMPLES_ARB 0x2042
  2568. #define WGL_CONTEXT_DEBUG_BIT_ARB 0x00000001
  2569. #define WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB 0x00000002
  2570. #define WGL_CONTEXT_PROFILE_MASK_ARB 0x9126
  2571. #define WGL_CONTEXT_CORE_PROFILE_BIT_ARB 0x00000001
  2572. #define WGL_CONTEXT_MAJOR_VERSION_ARB 0x2091
  2573. #define WGL_CONTEXT_MINOR_VERSION_ARB 0x2092
  2574. #define WGL_CONTEXT_FLAGS_ARB 0x2094
  2575. #define ERROR_INVALID_VERSION_ARB 0x2095
  2576. #define ERROR_INVALID_PROFILE_ARB 0x2096
  2577. #define ERROR_INCOMPATIBLE_DEVICE_CONTEXTS_ARB 0x2054
  2578. typedef BOOL (WINAPI * PFNWGLSWAPINTERVALEXTPROC)(int);
  2579. typedef BOOL (WINAPI * PFNWGLGETPIXELFORMATATTRIBIVARBPROC)(HDC,int,int,UINT,const int*,int*);
  2580. typedef const char* (WINAPI * PFNWGLGETEXTENSIONSSTRINGEXTPROC)(void);
  2581. typedef const char* (WINAPI * PFNWGLGETEXTENSIONSSTRINGARBPROC)(HDC);
  2582. typedef HGLRC (WINAPI * PFNWGLCREATECONTEXTATTRIBSARBPROC)(HDC,HGLRC,const int*);
  2583. typedef HGLRC (WINAPI * PFN_wglCreateContext)(HDC);
  2584. typedef BOOL (WINAPI * PFN_wglDeleteContext)(HGLRC);
  2585. typedef PROC (WINAPI * PFN_wglGetProcAddress)(LPCSTR);
  2586. typedef HDC (WINAPI * PFN_wglGetCurrentDC)(void);
  2587. typedef BOOL (WINAPI * PFN_wglMakeCurrent)(HDC,HGLRC);
  2588. typedef struct {
  2589. HINSTANCE opengl32;
  2590. HGLRC gl_ctx;
  2591. PFN_wglCreateContext CreateContext;
  2592. PFN_wglDeleteContext DeleteContext;
  2593. PFN_wglGetProcAddress GetProcAddress;
  2594. PFN_wglGetCurrentDC GetCurrentDC;
  2595. PFN_wglMakeCurrent MakeCurrent;
  2596. PFNWGLSWAPINTERVALEXTPROC SwapIntervalEXT;
  2597. PFNWGLGETPIXELFORMATATTRIBIVARBPROC GetPixelFormatAttribivARB;
  2598. PFNWGLGETEXTENSIONSSTRINGEXTPROC GetExtensionsStringEXT;
  2599. PFNWGLGETEXTENSIONSSTRINGARBPROC GetExtensionsStringARB;
  2600. PFNWGLCREATECONTEXTATTRIBSARBPROC CreateContextAttribsARB;
  2601. // special case glGetIntegerv
  2602. void (WINAPI *GetIntegerv)(uint32_t pname, int32_t* data);
  2603. bool ext_swap_control;
  2604. bool arb_multisample;
  2605. bool arb_pixel_format;
  2606. bool arb_create_context;
  2607. bool arb_create_context_profile;
  2608. HWND msg_hwnd;
  2609. HDC msg_dc;
  2610. } _sapp_wgl_t;
  2611. #endif // SOKOL_GLCORE
  2612. #endif // _SAPP_WIN32
  2613. #if defined(_SAPP_ANDROID)
  2614. typedef enum {
  2615. _SOKOL_ANDROID_MSG_CREATE,
  2616. _SOKOL_ANDROID_MSG_RESUME,
  2617. _SOKOL_ANDROID_MSG_PAUSE,
  2618. _SOKOL_ANDROID_MSG_FOCUS,
  2619. _SOKOL_ANDROID_MSG_NO_FOCUS,
  2620. _SOKOL_ANDROID_MSG_SET_NATIVE_WINDOW,
  2621. _SOKOL_ANDROID_MSG_SET_INPUT_QUEUE,
  2622. _SOKOL_ANDROID_MSG_DESTROY,
  2623. } _sapp_android_msg_t;
  2624. typedef struct {
  2625. pthread_t thread;
  2626. pthread_mutex_t mutex;
  2627. pthread_cond_t cond;
  2628. int read_from_main_fd;
  2629. int write_from_main_fd;
  2630. } _sapp_android_pt_t;
  2631. typedef struct {
  2632. ANativeWindow* window;
  2633. AInputQueue* input;
  2634. } _sapp_android_resources_t;
  2635. typedef struct {
  2636. ANativeActivity* activity;
  2637. _sapp_android_pt_t pt;
  2638. _sapp_android_resources_t pending;
  2639. _sapp_android_resources_t current;
  2640. ALooper* looper;
  2641. bool is_thread_started;
  2642. bool is_thread_stopping;
  2643. bool is_thread_stopped;
  2644. bool has_created;
  2645. bool has_resumed;
  2646. bool has_focus;
  2647. EGLConfig config;
  2648. EGLDisplay display;
  2649. EGLContext context;
  2650. EGLSurface surface;
  2651. } _sapp_android_t;
  2652. #endif // _SAPP_ANDROID
  2653. #if defined(_SAPP_LINUX)
  2654. #define _SAPP_X11_XDND_VERSION (5)
  2655. #define _SAPP_X11_MAX_X11_KEYCODES (256)
  2656. #define GLX_VENDOR 1
  2657. #define GLX_RGBA_BIT 0x00000001
  2658. #define GLX_WINDOW_BIT 0x00000001
  2659. #define GLX_DRAWABLE_TYPE 0x8010
  2660. #define GLX_RENDER_TYPE 0x8011
  2661. #define GLX_DOUBLEBUFFER 5
  2662. #define GLX_RED_SIZE 8
  2663. #define GLX_GREEN_SIZE 9
  2664. #define GLX_BLUE_SIZE 10
  2665. #define GLX_ALPHA_SIZE 11
  2666. #define GLX_DEPTH_SIZE 12
  2667. #define GLX_STENCIL_SIZE 13
  2668. #define GLX_SAMPLES 0x186a1
  2669. #define GLX_CONTEXT_CORE_PROFILE_BIT_ARB 0x00000001
  2670. #define GLX_CONTEXT_PROFILE_MASK_ARB 0x9126
  2671. #define GLX_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB 0x00000002
  2672. #define GLX_CONTEXT_MAJOR_VERSION_ARB 0x2091
  2673. #define GLX_CONTEXT_MINOR_VERSION_ARB 0x2092
  2674. #define GLX_CONTEXT_FLAGS_ARB 0x2094
  2675. typedef XID GLXWindow;
  2676. typedef XID GLXDrawable;
  2677. typedef struct __GLXFBConfig* GLXFBConfig;
  2678. typedef struct __GLXcontext* GLXContext;
  2679. typedef void (*__GLXextproc)(void);
  2680. typedef int (*PFNGLXGETFBCONFIGATTRIBPROC)(Display*,GLXFBConfig,int,int*);
  2681. typedef const char* (*PFNGLXGETCLIENTSTRINGPROC)(Display*,int);
  2682. typedef Bool (*PFNGLXQUERYEXTENSIONPROC)(Display*,int*,int*);
  2683. typedef Bool (*PFNGLXQUERYVERSIONPROC)(Display*,int*,int*);
  2684. typedef void (*PFNGLXDESTROYCONTEXTPROC)(Display*,GLXContext);
  2685. typedef Bool (*PFNGLXMAKECURRENTPROC)(Display*,GLXDrawable,GLXContext);
  2686. typedef void (*PFNGLXSWAPBUFFERSPROC)(Display*,GLXDrawable);
  2687. typedef const char* (*PFNGLXQUERYEXTENSIONSSTRINGPROC)(Display*,int);
  2688. typedef GLXFBConfig* (*PFNGLXGETFBCONFIGSPROC)(Display*,int,int*);
  2689. typedef __GLXextproc (* PFNGLXGETPROCADDRESSPROC)(const char *procName);
  2690. typedef void (*PFNGLXSWAPINTERVALEXTPROC)(Display*,GLXDrawable,int);
  2691. typedef XVisualInfo* (*PFNGLXGETVISUALFROMFBCONFIGPROC)(Display*,GLXFBConfig);
  2692. typedef GLXWindow (*PFNGLXCREATEWINDOWPROC)(Display*,GLXFBConfig,Window,const int*);
  2693. typedef void (*PFNGLXDESTROYWINDOWPROC)(Display*,GLXWindow);
  2694. typedef int (*PFNGLXSWAPINTERVALMESAPROC)(int);
  2695. typedef GLXContext (*PFNGLXCREATECONTEXTATTRIBSARBPROC)(Display*,GLXFBConfig,GLXContext,Bool,const int*);
  2696. typedef struct {
  2697. bool available;
  2698. int major_opcode;
  2699. int event_base;
  2700. int error_base;
  2701. int major;
  2702. int minor;
  2703. } _sapp_xi_t;
  2704. typedef struct {
  2705. int version;
  2706. Window source;
  2707. Atom format;
  2708. Atom XdndAware;
  2709. Atom XdndEnter;
  2710. Atom XdndPosition;
  2711. Atom XdndStatus;
  2712. Atom XdndActionCopy;
  2713. Atom XdndDrop;
  2714. Atom XdndFinished;
  2715. Atom XdndSelection;
  2716. Atom XdndTypeList;
  2717. Atom text_uri_list;
  2718. } _sapp_xdnd_t;
  2719. typedef struct {
  2720. uint8_t mouse_buttons;
  2721. Display* display;
  2722. int screen;
  2723. Window root;
  2724. Colormap colormap;
  2725. Window window;
  2726. Cursor hidden_cursor;
  2727. Cursor standard_cursors[_SAPP_MOUSECURSOR_NUM];
  2728. Cursor custom_cursors[_SAPP_MOUSECURSOR_NUM];
  2729. int window_state;
  2730. float dpi;
  2731. unsigned char error_code;
  2732. Atom UTF8_STRING;
  2733. Atom CLIPBOARD;
  2734. Atom TARGETS;
  2735. Atom WM_PROTOCOLS;
  2736. Atom WM_DELETE_WINDOW;
  2737. Atom WM_STATE;
  2738. Atom NET_WM_NAME;
  2739. Atom NET_WM_ICON_NAME;
  2740. Atom NET_WM_ICON;
  2741. Atom NET_WM_STATE;
  2742. Atom NET_WM_STATE_FULLSCREEN;
  2743. _sapp_xi_t xi;
  2744. _sapp_xdnd_t xdnd;
  2745. // XLib manual says keycodes are in the range [8, 255] inclusive.
  2746. // https://tronche.com/gui/x/xlib/input/keyboard-encoding.html
  2747. bool key_repeat[_SAPP_X11_MAX_X11_KEYCODES];
  2748. } _sapp_x11_t;
  2749. #if defined(_SAPP_GLX)
  2750. typedef struct {
  2751. void* libgl;
  2752. int major;
  2753. int minor;
  2754. int event_base;
  2755. int error_base;
  2756. GLXContext ctx;
  2757. GLXWindow window;
  2758. // GLX 1.3 functions
  2759. PFNGLXGETFBCONFIGSPROC GetFBConfigs;
  2760. PFNGLXGETFBCONFIGATTRIBPROC GetFBConfigAttrib;
  2761. PFNGLXGETCLIENTSTRINGPROC GetClientString;
  2762. PFNGLXQUERYEXTENSIONPROC QueryExtension;
  2763. PFNGLXQUERYVERSIONPROC QueryVersion;
  2764. PFNGLXDESTROYCONTEXTPROC DestroyContext;
  2765. PFNGLXMAKECURRENTPROC MakeCurrent;
  2766. PFNGLXSWAPBUFFERSPROC SwapBuffers;
  2767. PFNGLXQUERYEXTENSIONSSTRINGPROC QueryExtensionsString;
  2768. PFNGLXGETVISUALFROMFBCONFIGPROC GetVisualFromFBConfig;
  2769. PFNGLXCREATEWINDOWPROC CreateWindow;
  2770. PFNGLXDESTROYWINDOWPROC DestroyWindow;
  2771. // GLX 1.4 and extension functions
  2772. PFNGLXGETPROCADDRESSPROC GetProcAddress;
  2773. PFNGLXGETPROCADDRESSPROC GetProcAddressARB;
  2774. PFNGLXSWAPINTERVALEXTPROC SwapIntervalEXT;
  2775. PFNGLXSWAPINTERVALMESAPROC SwapIntervalMESA;
  2776. PFNGLXCREATECONTEXTATTRIBSARBPROC CreateContextAttribsARB;
  2777. // special case glGetIntegerv
  2778. void (*GetIntegerv)(uint32_t pname, int32_t* data);
  2779. // extension availability
  2780. bool EXT_swap_control;
  2781. bool MESA_swap_control;
  2782. bool ARB_multisample;
  2783. bool ARB_create_context;
  2784. bool ARB_create_context_profile;
  2785. } _sapp_glx_t;
  2786. #endif // _SAPP_GLX
  2787. #if defined(_SAPP_EGL)
  2788. typedef struct {
  2789. EGLDisplay display;
  2790. EGLContext context;
  2791. EGLSurface surface;
  2792. } _sapp_egl_t;
  2793. #endif // _SAPP_EGL
  2794. #endif // _SAPP_LINUX
  2795. #if defined(_SAPP_ANY_GL)
  2796. typedef struct {
  2797. uint32_t framebuffer;
  2798. } _sapp_gl_t;
  2799. #endif
  2800. typedef struct {
  2801. bool enabled;
  2802. int buf_size;
  2803. char* buffer;
  2804. } _sapp_clipboard_t;
  2805. typedef struct {
  2806. bool enabled;
  2807. int max_files;
  2808. int max_path_length;
  2809. int num_files;
  2810. int buf_size;
  2811. char* buffer;
  2812. } _sapp_drop_t;
  2813. typedef struct {
  2814. float x, y;
  2815. float dx, dy;
  2816. bool shown;
  2817. bool locked;
  2818. bool pos_valid;
  2819. sapp_mouse_cursor current_cursor;
  2820. } _sapp_mouse_t;
  2821. typedef struct {
  2822. sapp_desc desc;
  2823. bool valid;
  2824. bool fullscreen;
  2825. bool first_frame;
  2826. bool init_called;
  2827. bool cleanup_called;
  2828. bool quit_requested;
  2829. bool quit_ordered;
  2830. bool event_consumed;
  2831. bool html5_ask_leave_site;
  2832. bool onscreen_keyboard_shown;
  2833. int window_width;
  2834. int window_height;
  2835. int framebuffer_width;
  2836. int framebuffer_height;
  2837. int sample_count;
  2838. int swap_interval;
  2839. float dpi_scale;
  2840. uint64_t frame_count;
  2841. _sapp_timing_t timing;
  2842. sapp_event event;
  2843. _sapp_mouse_t mouse;
  2844. _sapp_clipboard_t clipboard;
  2845. _sapp_drop_t drop;
  2846. sapp_icon_desc default_icon_desc;
  2847. uint32_t* default_icon_pixels;
  2848. #if defined(SOKOL_WGPU)
  2849. _sapp_wgpu_t wgpu;
  2850. #endif
  2851. #if defined(SOKOL_VULKAN)
  2852. _sapp_vk_t vk;
  2853. #endif
  2854. #if defined(_SAPP_MACOS)
  2855. _sapp_macos_t macos;
  2856. #elif defined(_SAPP_IOS)
  2857. _sapp_ios_t ios;
  2858. #elif defined(_SAPP_EMSCRIPTEN)
  2859. _sapp_emsc_t emsc;
  2860. #elif defined(_SAPP_WIN32)
  2861. _sapp_win32_t win32;
  2862. #if defined(SOKOL_D3D11)
  2863. _sapp_d3d11_t d3d11;
  2864. #elif defined(SOKOL_GLCORE)
  2865. _sapp_wgl_t wgl;
  2866. #endif
  2867. #elif defined(_SAPP_ANDROID)
  2868. _sapp_android_t android;
  2869. #elif defined(_SAPP_LINUX)
  2870. _sapp_x11_t x11;
  2871. #if defined(_SAPP_GLX)
  2872. _sapp_glx_t glx;
  2873. #elif defined(_SAPP_EGL)
  2874. _sapp_egl_t egl;
  2875. #endif
  2876. #endif
  2877. #if defined(_SAPP_ANY_GL)
  2878. _sapp_gl_t gl;
  2879. #endif
  2880. char html5_canvas_selector[_SAPP_MAX_TITLE_LENGTH];
  2881. char window_title[_SAPP_MAX_TITLE_LENGTH]; // UTF-8
  2882. wchar_t window_title_wide[_SAPP_MAX_TITLE_LENGTH]; // UTF-32 or UCS-2 */
  2883. sapp_keycode keycodes[SAPP_MAX_KEYCODES];
  2884. bool custom_cursor_bound[_SAPP_MOUSECURSOR_NUM]; // true if a custom mouse cursor is bound on that slot
  2885. } _sapp_t;
  2886. static _sapp_t _sapp;
  2887. // ██ ██████ ██████ ██████ ██ ███ ██ ██████
  2888. // ██ ██ ██ ██ ██ ██ ████ ██ ██
  2889. // ██ ██ ██ ██ ███ ██ ███ ██ ██ ██ ██ ██ ███
  2890. // ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
  2891. // ███████ ██████ ██████ ██████ ██ ██ ████ ██████
  2892. //
  2893. // >>logging
  2894. #if defined(SOKOL_DEBUG)
  2895. #define _SAPP_LOGITEM_XMACRO(item,msg) #item ": " msg,
  2896. static const char* _sapp_log_messages[] = {
  2897. _SAPP_LOG_ITEMS
  2898. };
  2899. #undef _SAPP_LOGITEM_XMACRO
  2900. #endif // SOKOL_DEBUG
  2901. #define _SAPP_PANIC(code) _sapp_log(SAPP_LOGITEM_ ##code, 0, 0, __LINE__)
  2902. #define _SAPP_ERROR(code) _sapp_log(SAPP_LOGITEM_ ##code, 1, 0, __LINE__)
  2903. #define _SAPP_WARN(code) _sapp_log(SAPP_LOGITEM_ ##code, 2, 0, __LINE__)
  2904. #define _SAPP_INFO(code) _sapp_log(SAPP_LOGITEM_ ##code, 3, 0, __LINE__)
  2905. #define _SAPP_PANIC_MSG(code, msg) _sapp_log(SAPP_LOGITEM_ ##code, 0, msg, __LINE__)
  2906. #define _SAPP_ERROR_MSG(code, msg) _sapp_log(SAPP_LOGITEM_ ##code, 1, msg, __LINE__)
  2907. #define _SAPP_WARN_MSG(code, msg) _sapp_log(SAPP_LOGITEM_ ##code, 2, msg, __LINE__)
  2908. #define _SAPP_INFO_MSG(code, msg) _sapp_log(SAPP_LOGITEM_ ##code, 3, msg, __LINE__)
  2909. static void _sapp_log(sapp_log_item log_item, uint32_t log_level, const char* msg, uint32_t line_nr) {
  2910. if (_sapp.desc.logger.func) {
  2911. const char* filename = 0;
  2912. #if defined(SOKOL_DEBUG)
  2913. filename = __FILE__;
  2914. if (0 == msg) {
  2915. msg = _sapp_log_messages[log_item];
  2916. }
  2917. #endif
  2918. _sapp.desc.logger.func("sapp", log_level, (uint32_t)log_item, msg, line_nr, filename, _sapp.desc.logger.user_data);
  2919. } else {
  2920. // for log level PANIC it would be 'undefined behaviour' to continue
  2921. if (log_level == 0) {
  2922. abort();
  2923. }
  2924. }
  2925. }
  2926. // ███ ███ ███████ ███ ███ ██████ ██████ ██ ██
  2927. // ████ ████ ██ ████ ████ ██ ██ ██ ██ ██ ██
  2928. // ██ ████ ██ █████ ██ ████ ██ ██ ██ ██████ ████
  2929. // ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
  2930. // ██ ██ ███████ ██ ██ ██████ ██ ██ ██
  2931. //
  2932. // >>memory
  2933. _SOKOL_PRIVATE void _sapp_clear(void* ptr, size_t size) {
  2934. SOKOL_ASSERT(ptr && (size > 0));
  2935. memset(ptr, 0, size);
  2936. }
  2937. _SOKOL_PRIVATE void* _sapp_malloc(size_t size) {
  2938. SOKOL_ASSERT(size > 0);
  2939. void* ptr;
  2940. if (_sapp.desc.allocator.alloc_fn) {
  2941. ptr = _sapp.desc.allocator.alloc_fn(size, _sapp.desc.allocator.user_data);
  2942. } else {
  2943. ptr = malloc(size);
  2944. }
  2945. if (0 == ptr) {
  2946. _SAPP_PANIC(MALLOC_FAILED);
  2947. }
  2948. return ptr;
  2949. }
  2950. _SOKOL_PRIVATE void* _sapp_malloc_clear(size_t size) {
  2951. void* ptr = _sapp_malloc(size);
  2952. _sapp_clear(ptr, size);
  2953. return ptr;
  2954. }
  2955. _SOKOL_PRIVATE void _sapp_free(void* ptr) {
  2956. if (_sapp.desc.allocator.free_fn) {
  2957. _sapp.desc.allocator.free_fn(ptr, _sapp.desc.allocator.user_data);
  2958. } else {
  2959. free(ptr);
  2960. }
  2961. }
  2962. // ██ ██ ███████ ██ ██████ ███████ ██████ ███████
  2963. // ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
  2964. // ███████ █████ ██ ██████ █████ ██████ ███████
  2965. // ██ ██ ██ ██ ██ ██ ██ ██ ██
  2966. // ██ ██ ███████ ███████ ██ ███████ ██ ██ ███████
  2967. //
  2968. // >>helpers
  2969. // round float to int and at least 1
  2970. _SOKOL_PRIVATE int _sapp_roundf_gzero(float f) {
  2971. int val = (int)roundf(f);
  2972. if (val <= 0) {
  2973. val = 1;
  2974. }
  2975. return val;
  2976. }
  2977. _SOKOL_PRIVATE void _sapp_call_init(void) {
  2978. if (_sapp.desc.init_cb) {
  2979. _sapp.desc.init_cb();
  2980. } else if (_sapp.desc.init_userdata_cb) {
  2981. _sapp.desc.init_userdata_cb(_sapp.desc.user_data);
  2982. }
  2983. _sapp.init_called = true;
  2984. }
  2985. _SOKOL_PRIVATE void _sapp_call_frame(void) {
  2986. if (_sapp.init_called && !_sapp.cleanup_called) {
  2987. if (_sapp.desc.frame_cb) {
  2988. _sapp.desc.frame_cb();
  2989. } else if (_sapp.desc.frame_userdata_cb) {
  2990. _sapp.desc.frame_userdata_cb(_sapp.desc.user_data);
  2991. }
  2992. }
  2993. }
  2994. _SOKOL_PRIVATE void _sapp_call_cleanup(void) {
  2995. if (!_sapp.cleanup_called) {
  2996. if (_sapp.desc.cleanup_cb) {
  2997. _sapp.desc.cleanup_cb();
  2998. } else if (_sapp.desc.cleanup_userdata_cb) {
  2999. _sapp.desc.cleanup_userdata_cb(_sapp.desc.user_data);
  3000. }
  3001. _sapp.cleanup_called = true;
  3002. }
  3003. }
  3004. _SOKOL_PRIVATE bool _sapp_call_event(const sapp_event* e) {
  3005. if (!_sapp.cleanup_called) {
  3006. if (_sapp.desc.event_cb) {
  3007. _sapp.desc.event_cb(e);
  3008. } else if (_sapp.desc.event_userdata_cb) {
  3009. _sapp.desc.event_userdata_cb(e, _sapp.desc.user_data);
  3010. }
  3011. }
  3012. if (_sapp.event_consumed) {
  3013. _sapp.event_consumed = false;
  3014. return true;
  3015. } else {
  3016. return false;
  3017. }
  3018. }
  3019. _SOKOL_PRIVATE char* _sapp_dropped_file_path_ptr(int index) {
  3020. SOKOL_ASSERT(_sapp.drop.buffer);
  3021. SOKOL_ASSERT((index >= 0) && (index <= _sapp.drop.max_files));
  3022. int offset = index * _sapp.drop.max_path_length;
  3023. SOKOL_ASSERT(offset < _sapp.drop.buf_size);
  3024. return &_sapp.drop.buffer[offset];
  3025. }
  3026. /* Copy a string (either zero-terminated or with explicit length)
  3027. into a fixed size buffer with guaranteed zero-termination.
  3028. Return false if the string didn't fit into the buffer and had to be clamped.
  3029. FIXME: Currently UTF-8 strings might become invalid if the string
  3030. is clamped, because the last zero-byte might be written into
  3031. the middle of a multi-byte sequence.
  3032. */
  3033. _SOKOL_PRIVATE bool _sapp_strcpy_range(const char* src, size_t src_len, char* dst, size_t dst_buf_len) {
  3034. SOKOL_ASSERT(src && dst && (dst_buf_len > 0));
  3035. if (0 == src_len) {
  3036. src_len = dst_buf_len;
  3037. }
  3038. char* const end = &(dst[dst_buf_len-1]);
  3039. char c = 0;
  3040. for (size_t i = 0; i < dst_buf_len; i++) {
  3041. c = *src;
  3042. if (i >= src_len) {
  3043. c = 0;
  3044. }
  3045. if (c != 0) {
  3046. src++;
  3047. }
  3048. *dst++ = c;
  3049. }
  3050. // truncated?
  3051. if (c != 0) {
  3052. *end = 0;
  3053. return false;
  3054. } else {
  3055. return true;
  3056. }
  3057. }
  3058. _SOKOL_PRIVATE bool _sapp_strcpy(const char* src, char* dst, size_t dst_buf_len) {
  3059. return _sapp_strcpy_range(src, 0, dst, dst_buf_len);
  3060. }
  3061. _SOKOL_PRIVATE sapp_desc _sapp_desc_defaults(const sapp_desc* desc) {
  3062. SOKOL_ASSERT((desc->allocator.alloc_fn && desc->allocator.free_fn) || (!desc->allocator.alloc_fn && !desc->allocator.free_fn));
  3063. sapp_desc res = *desc;
  3064. res.sample_count = _sapp_def(res.sample_count, 1);
  3065. res.swap_interval = _sapp_def(res.swap_interval, 1);
  3066. if (0 == res.gl.major_version) {
  3067. #if defined(SOKOL_GLCORE)
  3068. res.gl.major_version = 4;
  3069. #if defined(_SAPP_APPLE)
  3070. res.gl.minor_version = 1;
  3071. #else
  3072. res.gl.minor_version = 3;
  3073. #endif
  3074. #elif defined(SOKOL_GLES3)
  3075. res.gl.major_version = 3;
  3076. #if defined(_SAPP_ANDROID) || defined(_SAPP_LINUX)
  3077. res.gl.minor_version = 1;
  3078. #else
  3079. res.gl.minor_version = 0;
  3080. #endif
  3081. #endif
  3082. }
  3083. res.html5.canvas_selector = _sapp_def(res.html5.canvas_selector, "#canvas");
  3084. res.clipboard_size = _sapp_def(res.clipboard_size, 8192);
  3085. res.max_dropped_files = _sapp_def(res.max_dropped_files, 1);
  3086. res.max_dropped_file_path_length = _sapp_def(res.max_dropped_file_path_length, 2048);
  3087. res.window_title = _sapp_def(res.window_title, "sokol");
  3088. return res;
  3089. }
  3090. _SOKOL_PRIVATE void _sapp_init_state(const sapp_desc* desc) {
  3091. SOKOL_ASSERT(desc);
  3092. SOKOL_ASSERT(desc->width >= 0);
  3093. SOKOL_ASSERT(desc->height >= 0);
  3094. SOKOL_ASSERT(desc->sample_count >= 0);
  3095. SOKOL_ASSERT(desc->swap_interval >= 0);
  3096. SOKOL_ASSERT(desc->clipboard_size >= 0);
  3097. SOKOL_ASSERT(desc->max_dropped_files >= 0);
  3098. SOKOL_ASSERT(desc->max_dropped_file_path_length >= 0);
  3099. _SAPP_CLEAR_ARC_STRUCT(_sapp_t, _sapp);
  3100. _sapp.desc = _sapp_desc_defaults(desc);
  3101. _sapp.first_frame = true;
  3102. // NOTE: _sapp.desc.width/height may be 0! Platform backends need to deal with this
  3103. _sapp.window_width = _sapp.desc.width;
  3104. _sapp.window_height = _sapp.desc.height;
  3105. _sapp.framebuffer_width = _sapp.window_width;
  3106. _sapp.framebuffer_height = _sapp.window_height;
  3107. _sapp.sample_count = _sapp.desc.sample_count;
  3108. _sapp.swap_interval = _sapp.desc.swap_interval;
  3109. _sapp_strcpy(_sapp.desc.html5.canvas_selector, _sapp.html5_canvas_selector, sizeof(_sapp.html5_canvas_selector));
  3110. _sapp.desc.html5.canvas_selector = _sapp.html5_canvas_selector;
  3111. _sapp.html5_ask_leave_site = _sapp.desc.html5.ask_leave_site;
  3112. _sapp.clipboard.enabled = _sapp.desc.enable_clipboard;
  3113. if (_sapp.clipboard.enabled) {
  3114. _sapp.clipboard.buf_size = _sapp.desc.clipboard_size;
  3115. _sapp.clipboard.buffer = (char*) _sapp_malloc_clear((size_t)_sapp.clipboard.buf_size);
  3116. }
  3117. _sapp.drop.enabled = _sapp.desc.enable_dragndrop;
  3118. if (_sapp.drop.enabled) {
  3119. _sapp.drop.max_files = _sapp.desc.max_dropped_files;
  3120. _sapp.drop.max_path_length = _sapp.desc.max_dropped_file_path_length;
  3121. _sapp.drop.buf_size = _sapp.drop.max_files * _sapp.drop.max_path_length;
  3122. _sapp.drop.buffer = (char*) _sapp_malloc_clear((size_t)_sapp.drop.buf_size);
  3123. }
  3124. _sapp_strcpy(_sapp.desc.window_title, _sapp.window_title, sizeof(_sapp.window_title));
  3125. _sapp.desc.window_title = _sapp.window_title;
  3126. _sapp.dpi_scale = 1.0f;
  3127. _sapp.fullscreen = _sapp.desc.fullscreen;
  3128. _sapp.mouse.shown = true;
  3129. _sapp_timing_init(&_sapp.timing);
  3130. }
  3131. _SOKOL_PRIVATE void _sapp_discard_state(void) {
  3132. if (_sapp.clipboard.enabled) {
  3133. SOKOL_ASSERT(_sapp.clipboard.buffer);
  3134. _sapp_free((void*)_sapp.clipboard.buffer);
  3135. }
  3136. if (_sapp.drop.enabled) {
  3137. SOKOL_ASSERT(_sapp.drop.buffer);
  3138. _sapp_free((void*)_sapp.drop.buffer);
  3139. }
  3140. if (_sapp.default_icon_pixels) {
  3141. _sapp_free((void*)_sapp.default_icon_pixels);
  3142. }
  3143. for (int i = 0; i < _SAPP_MOUSECURSOR_NUM; i++) {
  3144. sapp_unbind_mouse_cursor_image((sapp_mouse_cursor) i);
  3145. }
  3146. _SAPP_CLEAR_ARC_STRUCT(_sapp_t, _sapp);
  3147. }
  3148. _SOKOL_PRIVATE void _sapp_init_event(sapp_event_type type) {
  3149. _sapp_clear(&_sapp.event, sizeof(_sapp.event));
  3150. _sapp.event.type = type;
  3151. _sapp.event.frame_count = _sapp.frame_count;
  3152. _sapp.event.mouse_button = SAPP_MOUSEBUTTON_INVALID;
  3153. _sapp.event.window_width = _sapp.window_width;
  3154. _sapp.event.window_height = _sapp.window_height;
  3155. _sapp.event.framebuffer_width = _sapp.framebuffer_width;
  3156. _sapp.event.framebuffer_height = _sapp.framebuffer_height;
  3157. _sapp.event.mouse_x = _sapp.mouse.x;
  3158. _sapp.event.mouse_y = _sapp.mouse.y;
  3159. _sapp.event.mouse_dx = _sapp.mouse.dx;
  3160. _sapp.event.mouse_dy = _sapp.mouse.dy;
  3161. }
  3162. _SOKOL_PRIVATE bool _sapp_events_enabled(void) {
  3163. /* only send events when an event callback is set, and the init function was called */
  3164. return (_sapp.desc.event_cb || _sapp.desc.event_userdata_cb) && _sapp.init_called;
  3165. }
  3166. _SOKOL_PRIVATE sapp_keycode _sapp_translate_key(int scan_code) {
  3167. if ((scan_code >= 0) && (scan_code < SAPP_MAX_KEYCODES)) {
  3168. return _sapp.keycodes[scan_code];
  3169. } else {
  3170. return SAPP_KEYCODE_INVALID;
  3171. }
  3172. }
  3173. _SOKOL_PRIVATE void _sapp_clear_drop_buffer(void) {
  3174. if (_sapp.drop.enabled) {
  3175. SOKOL_ASSERT(_sapp.drop.buffer);
  3176. _sapp_clear(_sapp.drop.buffer, (size_t)_sapp.drop.buf_size);
  3177. }
  3178. }
  3179. _SOKOL_PRIVATE void _sapp_frame(void) {
  3180. if (_sapp.first_frame) {
  3181. _sapp.first_frame = false;
  3182. _sapp_call_init();
  3183. }
  3184. _sapp_call_frame();
  3185. _sapp.frame_count++;
  3186. }
  3187. _SOKOL_PRIVATE bool _sapp_image_validate(const sapp_image_desc* desc) {
  3188. SOKOL_ASSERT(desc->width > 0);
  3189. SOKOL_ASSERT(desc->height > 0);
  3190. SOKOL_ASSERT(desc->pixels.ptr != 0);
  3191. SOKOL_ASSERT(desc->pixels.size > 0);
  3192. const size_t wh_size = (size_t)(desc->width * desc->height) * sizeof(uint32_t);
  3193. if (wh_size != desc->pixels.size) {
  3194. _SAPP_ERROR(IMAGE_DATA_SIZE_MISMATCH);
  3195. return false;
  3196. }
  3197. return true;
  3198. }
  3199. _SOKOL_PRIVATE int _sapp_image_bestmatch(const sapp_image_desc image_descs[], int num_images, int width, int height) {
  3200. int least_diff = 0x7FFFFFFF;
  3201. int least_index = 0;
  3202. for (int i = 0; i < num_images; i++) {
  3203. int diff = (image_descs[i].width * image_descs[i].height) - (width * height);
  3204. if (diff < 0) {
  3205. diff = -diff;
  3206. }
  3207. if (diff < least_diff) {
  3208. least_diff = diff;
  3209. least_index = i;
  3210. }
  3211. }
  3212. return least_index;
  3213. }
  3214. _SOKOL_PRIVATE int _sapp_icon_num_images(const sapp_icon_desc* desc) {
  3215. int index = 0;
  3216. for (; index < SAPP_MAX_ICONIMAGES; index++) {
  3217. if (0 == desc->images[index].pixels.ptr) {
  3218. break;
  3219. }
  3220. }
  3221. return index;
  3222. }
  3223. _SOKOL_PRIVATE bool _sapp_validate_icon_desc(const sapp_icon_desc* desc, int num_images) {
  3224. SOKOL_ASSERT(num_images <= SAPP_MAX_ICONIMAGES);
  3225. for (int i = 0; i < num_images; i++) {
  3226. const sapp_image_desc* img_desc = &desc->images[i];
  3227. if (!_sapp_image_validate(img_desc)) {
  3228. return false;
  3229. }
  3230. }
  3231. return true;
  3232. }
  3233. _SOKOL_PRIVATE void _sapp_setup_default_icon(void) {
  3234. SOKOL_ASSERT(0 == _sapp.default_icon_pixels);
  3235. const int num_icons = 3;
  3236. const int icon_sizes[3] = { 16, 32, 64 }; // must be multiple of 8!
  3237. // allocate a pixel buffer for all icon pixels
  3238. int all_num_pixels = 0;
  3239. for (int i = 0; i < num_icons; i++) {
  3240. all_num_pixels += icon_sizes[i] * icon_sizes[i];
  3241. }
  3242. _sapp.default_icon_pixels = (uint32_t*) _sapp_malloc_clear((size_t)all_num_pixels * sizeof(uint32_t));
  3243. // initialize default_icon_desc struct
  3244. uint32_t* dst = _sapp.default_icon_pixels;
  3245. const uint32_t* dst_end = dst + all_num_pixels;
  3246. (void)dst_end; // silence unused warning in release mode
  3247. for (int i = 0; i < num_icons; i++) {
  3248. const int dim = (int) icon_sizes[i];
  3249. const int num_pixels = dim * dim;
  3250. sapp_image_desc* img_desc = &_sapp.default_icon_desc.images[i];
  3251. img_desc->width = dim;
  3252. img_desc->height = dim;
  3253. img_desc->pixels.ptr = dst;
  3254. img_desc->pixels.size = (size_t)num_pixels * sizeof(uint32_t);
  3255. dst += num_pixels;
  3256. }
  3257. SOKOL_ASSERT(dst == dst_end);
  3258. // Amstrad CPC font 'S'
  3259. const uint8_t tile[8] = {
  3260. 0x3C,
  3261. 0x66,
  3262. 0x60,
  3263. 0x3C,
  3264. 0x06,
  3265. 0x66,
  3266. 0x3C,
  3267. 0x00,
  3268. };
  3269. // rainbow colors
  3270. const uint32_t colors[8] = {
  3271. 0xFF4370FF,
  3272. 0xFF26A7FF,
  3273. 0xFF58EEFF,
  3274. 0xFF57E1D4,
  3275. 0xFF65CC9C,
  3276. 0xFF6ABB66,
  3277. 0xFFF5A542,
  3278. 0xFFC2577E,
  3279. };
  3280. dst = _sapp.default_icon_pixels;
  3281. const uint32_t blank = 0x00FFFFFF;
  3282. const uint32_t shadow = 0xFF000000;
  3283. for (int i = 0; i < num_icons; i++) {
  3284. const int dim = icon_sizes[i];
  3285. SOKOL_ASSERT((dim % 8) == 0);
  3286. const int scale = dim / 8;
  3287. for (int ty = 0, y = 0; ty < 8; ty++) {
  3288. const uint32_t color = colors[ty];
  3289. for (int sy = 0; sy < scale; sy++, y++) {
  3290. uint8_t bits = tile[ty];
  3291. for (int tx = 0, x = 0; tx < 8; tx++, bits<<=1) {
  3292. uint32_t pixel = (0 == (bits & 0x80)) ? blank : color;
  3293. for (int sx = 0; sx < scale; sx++, x++) {
  3294. SOKOL_ASSERT(dst < dst_end);
  3295. *dst++ = pixel;
  3296. }
  3297. }
  3298. }
  3299. }
  3300. }
  3301. SOKOL_ASSERT(dst == dst_end);
  3302. // right shadow
  3303. dst = _sapp.default_icon_pixels;
  3304. for (int i = 0; i < num_icons; i++) {
  3305. const int dim = icon_sizes[i];
  3306. for (int y = 0; y < dim; y++) {
  3307. uint32_t prev_color = blank;
  3308. for (int x = 0; x < dim; x++) {
  3309. const int dst_index = y * dim + x;
  3310. const uint32_t cur_color = dst[dst_index];
  3311. if ((cur_color == blank) && (prev_color != blank)) {
  3312. dst[dst_index] = shadow;
  3313. }
  3314. prev_color = cur_color;
  3315. }
  3316. }
  3317. dst += dim * dim;
  3318. }
  3319. SOKOL_ASSERT(dst == dst_end);
  3320. // bottom shadow
  3321. dst = _sapp.default_icon_pixels;
  3322. for (int i = 0; i < num_icons; i++) {
  3323. const int dim = icon_sizes[i];
  3324. for (int x = 0; x < dim; x++) {
  3325. uint32_t prev_color = blank;
  3326. for (int y = 0; y < dim; y++) {
  3327. const int dst_index = y * dim + x;
  3328. const uint32_t cur_color = dst[dst_index];
  3329. if ((cur_color == blank) && (prev_color != blank)) {
  3330. dst[dst_index] = shadow;
  3331. }
  3332. prev_color = cur_color;
  3333. }
  3334. }
  3335. dst += dim * dim;
  3336. }
  3337. SOKOL_ASSERT(dst == dst_end);
  3338. }
  3339. // ██ ██ ██████ ██████ ██ ██
  3340. // ██ ██ ██ ██ ██ ██ ██
  3341. // ██ █ ██ ██ ███ ██████ ██ ██
  3342. // ██ ███ ██ ██ ██ ██ ██ ██
  3343. // ███ ███ ██████ ██ ██████
  3344. //
  3345. // >>wgpu
  3346. #if defined(SOKOL_WGPU)
  3347. _SOKOL_PRIVATE WGPUStringView _sapp_wgpu_stringview(const char* str) {
  3348. WGPUStringView res;
  3349. if (str) {
  3350. res.data = str;
  3351. res.length = strlen(str);
  3352. } else {
  3353. res.data = 0;
  3354. res.length = 0;
  3355. }
  3356. return res;
  3357. }
  3358. _SOKOL_PRIVATE WGPUCallbackMode _sapp_wgpu_callbackmode(void) {
  3359. #if defined(_SAPP_WGPU_HAS_WAIT)
  3360. return WGPUCallbackMode_WaitAnyOnly;
  3361. #else
  3362. return WGPUCallbackMode_AllowProcessEvents;
  3363. #endif
  3364. }
  3365. _SOKOL_PRIVATE void _sapp_wgpu_await(WGPUFuture future) {
  3366. #if defined(_SAPP_WGPU_HAS_WAIT)
  3367. SOKOL_ASSERT(_sapp.wgpu.instance);
  3368. _SAPP_STRUCT(WGPUFutureWaitInfo, wait_info);
  3369. wait_info.future = future;
  3370. WGPUWaitStatus res = wgpuInstanceWaitAny(_sapp.wgpu.instance, 1, &wait_info, UINT64_MAX);
  3371. SOKOL_ASSERT(res == WGPUWaitStatus_Success); _SOKOL_UNUSED(res);
  3372. #else
  3373. // this code path should never be called
  3374. _SOKOL_UNUSED(future);
  3375. SOKOL_ASSERT(false);
  3376. #endif
  3377. }
  3378. _SOKOL_PRIVATE WGPUTextureFormat _sapp_wgpu_pick_render_format(size_t count, const WGPUTextureFormat* formats) {
  3379. // NOTE: only accept non-SRGB formats until sokol_app.h gets proper SRGB support
  3380. SOKOL_ASSERT((count > 0) && formats);
  3381. for (size_t i = 0; i < count; i++) {
  3382. const WGPUTextureFormat fmt = formats[i];
  3383. switch (fmt) {
  3384. case WGPUTextureFormat_RGBA8Unorm:
  3385. case WGPUTextureFormat_BGRA8Unorm:
  3386. return fmt;
  3387. default: break;
  3388. }
  3389. }
  3390. // FIXME: fallback might still return an SRGB format
  3391. return formats[0];
  3392. }
  3393. _SOKOL_PRIVATE void _sapp_wgpu_create_swapchain(bool called_from_resize) {
  3394. SOKOL_ASSERT(_sapp.wgpu.instance);
  3395. SOKOL_ASSERT(_sapp.wgpu.device);
  3396. SOKOL_ASSERT(0 == _sapp.wgpu.msaa_tex);
  3397. SOKOL_ASSERT(0 == _sapp.wgpu.msaa_view);
  3398. SOKOL_ASSERT(0 == _sapp.wgpu.depth_stencil_tex);
  3399. SOKOL_ASSERT(0 == _sapp.wgpu.depth_stencil_view);
  3400. if (!called_from_resize) {
  3401. SOKOL_ASSERT(0 == _sapp.wgpu.surface);
  3402. _SAPP_STRUCT(WGPUSurfaceDescriptor, surf_desc);
  3403. #if defined (_SAPP_EMSCRIPTEN)
  3404. _SAPP_STRUCT(WGPUEmscriptenSurfaceSourceCanvasHTMLSelector, html_canvas_desc);
  3405. html_canvas_desc.chain.sType = WGPUSType_EmscriptenSurfaceSourceCanvasHTMLSelector;
  3406. html_canvas_desc.selector = _sapp_wgpu_stringview(_sapp.html5_canvas_selector);
  3407. surf_desc.nextInChain = &html_canvas_desc.chain;
  3408. #elif defined(_SAPP_MACOS)
  3409. _SAPP_STRUCT(WGPUSurfaceSourceMetalLayer, from_metal_layer);
  3410. from_metal_layer.chain.sType = WGPUSType_SurfaceSourceMetalLayer;
  3411. from_metal_layer.layer = _sapp.macos.view.layer;
  3412. surf_desc.nextInChain = &from_metal_layer.chain;
  3413. #elif defined(_SAPP_WIN32)
  3414. _SAPP_STRUCT(WGPUSurfaceSourceWindowsHWND, from_hwnd);
  3415. from_hwnd.chain.sType = WGPUSType_SurfaceSourceWindowsHWND;
  3416. from_hwnd.hinstance = GetModuleHandleW(NULL);
  3417. from_hwnd.hwnd = _sapp.win32.hwnd;
  3418. surf_desc.nextInChain = &from_hwnd.chain;
  3419. #elif defined(_SAPP_LINUX)
  3420. _SAPP_STRUCT(WGPUSurfaceSourceXlibWindow, from_xlib);
  3421. from_xlib.chain.sType = WGPUSType_SurfaceSourceXlibWindow;
  3422. from_xlib.display = _sapp.x11.display;
  3423. from_xlib.window = _sapp.x11.window;
  3424. surf_desc.nextInChain = &from_xlib.chain;
  3425. #else
  3426. #error "sokol_app.h: unsupported WebGPU platform"
  3427. #endif
  3428. _sapp.wgpu.surface = wgpuInstanceCreateSurface(_sapp.wgpu.instance, &surf_desc);
  3429. if (0 == _sapp.wgpu.surface) {
  3430. _SAPP_PANIC(WGPU_SWAPCHAIN_CREATE_SURFACE_FAILED);
  3431. }
  3432. _SAPP_STRUCT(WGPUSurfaceCapabilities, surf_caps);
  3433. WGPUStatus caps_status = wgpuSurfaceGetCapabilities(_sapp.wgpu.surface, _sapp.wgpu.adapter, &surf_caps);
  3434. if (caps_status != WGPUStatus_Success) {
  3435. _SAPP_PANIC(WGPU_SWAPCHAIN_SURFACE_GET_CAPABILITIES_FAILED);
  3436. }
  3437. _sapp.wgpu.render_format = _sapp_wgpu_pick_render_format(surf_caps.formatCount, surf_caps.formats);
  3438. }
  3439. SOKOL_ASSERT(_sapp.wgpu.surface);
  3440. _SAPP_STRUCT(WGPUSurfaceConfiguration, surf_conf);
  3441. surf_conf.device = _sapp.wgpu.device;
  3442. surf_conf.format = _sapp.wgpu.render_format;
  3443. surf_conf.usage = WGPUTextureUsage_RenderAttachment;
  3444. surf_conf.width = (uint32_t)_sapp.framebuffer_width;
  3445. surf_conf.height = (uint32_t)_sapp.framebuffer_height;
  3446. surf_conf.alphaMode = WGPUCompositeAlphaMode_Opaque;
  3447. #if defined(_SAPP_EMSCRIPTEN)
  3448. // FIXME: make this further configurable?
  3449. if (_sapp.desc.html5.premultiplied_alpha) {
  3450. surf_conf.alphaMode = WGPUCompositeAlphaMode_Premultiplied;
  3451. }
  3452. #endif
  3453. surf_conf.presentMode = WGPUPresentMode_Fifo;
  3454. wgpuSurfaceConfigure(_sapp.wgpu.surface, &surf_conf);
  3455. _SAPP_STRUCT(WGPUTextureDescriptor, ds_desc);
  3456. ds_desc.usage = WGPUTextureUsage_RenderAttachment;
  3457. ds_desc.dimension = WGPUTextureDimension_2D;
  3458. ds_desc.size.width = (uint32_t)_sapp.framebuffer_width;
  3459. ds_desc.size.height = (uint32_t)_sapp.framebuffer_height;
  3460. ds_desc.size.depthOrArrayLayers = 1;
  3461. ds_desc.format = WGPUTextureFormat_Depth32FloatStencil8;
  3462. ds_desc.mipLevelCount = 1;
  3463. ds_desc.sampleCount = (uint32_t)_sapp.sample_count;
  3464. _sapp.wgpu.depth_stencil_tex = wgpuDeviceCreateTexture(_sapp.wgpu.device, &ds_desc);
  3465. if (0 == _sapp.wgpu.depth_stencil_tex) {
  3466. _SAPP_PANIC(WGPU_SWAPCHAIN_CREATE_DEPTH_STENCIL_TEXTURE_FAILED);
  3467. }
  3468. _sapp.wgpu.depth_stencil_view = wgpuTextureCreateView(_sapp.wgpu.depth_stencil_tex, 0);
  3469. if (0 == _sapp.wgpu.depth_stencil_view) {
  3470. _SAPP_PANIC(WGPU_SWAPCHAIN_CREATE_DEPTH_STENCIL_VIEW_FAILED);
  3471. }
  3472. if (_sapp.sample_count > 1) {
  3473. _SAPP_STRUCT(WGPUTextureDescriptor, msaa_desc);
  3474. msaa_desc.usage = WGPUTextureUsage_RenderAttachment;
  3475. msaa_desc.dimension = WGPUTextureDimension_2D;
  3476. msaa_desc.size.width = (uint32_t)_sapp.framebuffer_width;
  3477. msaa_desc.size.height = (uint32_t)_sapp.framebuffer_height;
  3478. msaa_desc.size.depthOrArrayLayers = 1;
  3479. msaa_desc.format = _sapp.wgpu.render_format;
  3480. msaa_desc.mipLevelCount = 1;
  3481. msaa_desc.sampleCount = (uint32_t)_sapp.sample_count;
  3482. _sapp.wgpu.msaa_tex = wgpuDeviceCreateTexture(_sapp.wgpu.device, &msaa_desc);
  3483. if (0 == _sapp.wgpu.msaa_tex) {
  3484. _SAPP_PANIC(WGPU_SWAPCHAIN_CREATE_MSAA_TEXTURE_FAILED);
  3485. }
  3486. _sapp.wgpu.msaa_view = wgpuTextureCreateView(_sapp.wgpu.msaa_tex, 0);
  3487. if (0 == _sapp.wgpu.msaa_view) {
  3488. _SAPP_PANIC(WGPU_SWAPCHAIN_CREATE_MSAA_VIEW_FAILED);
  3489. }
  3490. }
  3491. }
  3492. _SOKOL_PRIVATE void _sapp_wgpu_discard_swapchain(bool called_from_resize) {
  3493. if (_sapp.wgpu.msaa_view) {
  3494. wgpuTextureViewRelease(_sapp.wgpu.msaa_view);
  3495. _sapp.wgpu.msaa_view = 0;
  3496. }
  3497. if (_sapp.wgpu.msaa_tex) {
  3498. wgpuTextureRelease(_sapp.wgpu.msaa_tex);
  3499. _sapp.wgpu.msaa_tex = 0;
  3500. }
  3501. if (_sapp.wgpu.depth_stencil_view) {
  3502. wgpuTextureViewRelease(_sapp.wgpu.depth_stencil_view);
  3503. _sapp.wgpu.depth_stencil_view = 0;
  3504. }
  3505. if (_sapp.wgpu.depth_stencil_tex) {
  3506. wgpuTextureRelease(_sapp.wgpu.depth_stencil_tex);
  3507. _sapp.wgpu.depth_stencil_tex = 0;
  3508. }
  3509. if (!called_from_resize) {
  3510. if (_sapp.wgpu.surface) {
  3511. wgpuSurfaceRelease(_sapp.wgpu.surface);
  3512. _sapp.wgpu.surface = 0;
  3513. }
  3514. }
  3515. }
  3516. _SOKOL_PRIVATE void _sapp_wgpu_swapchain_next(void) {
  3517. SOKOL_ASSERT(0 == _sapp.wgpu.swapchain_view);
  3518. _SAPP_STRUCT(WGPUSurfaceTexture, surf_tex);
  3519. wgpuSurfaceGetCurrentTexture(_sapp.wgpu.surface, &surf_tex);
  3520. switch (surf_tex.status) {
  3521. case WGPUSurfaceGetCurrentTextureStatus_SuccessOptimal:
  3522. case WGPUSurfaceGetCurrentTextureStatus_SuccessSuboptimal:
  3523. // all ok
  3524. break;
  3525. case WGPUSurfaceGetCurrentTextureStatus_Timeout:
  3526. case WGPUSurfaceGetCurrentTextureStatus_Outdated:
  3527. case WGPUSurfaceGetCurrentTextureStatus_Lost:
  3528. if (surf_tex.texture) {
  3529. wgpuTextureRelease(surf_tex.texture);
  3530. }
  3531. _sapp_wgpu_discard_swapchain(false);
  3532. _sapp_wgpu_create_swapchain(false);
  3533. // FIXME: currently this will assert in the caller
  3534. return;
  3535. case WGPUSurfaceGetCurrentTextureStatus_Error:
  3536. default:
  3537. _SAPP_PANIC(WGPU_SWAPCHAIN_GETCURRENTTEXTURE_FAILED);
  3538. break;
  3539. }
  3540. _sapp.wgpu.swapchain_view = wgpuTextureCreateView(surf_tex.texture, 0);
  3541. SOKOL_ASSERT(_sapp.wgpu.swapchain_view);
  3542. }
  3543. _SOKOL_PRIVATE void _sapp_wgpu_swapchain_size_changed(void) {
  3544. if (_sapp.wgpu.surface) {
  3545. _sapp_wgpu_discard_swapchain(true);
  3546. _sapp_wgpu_create_swapchain(true);
  3547. }
  3548. }
  3549. _SOKOL_PRIVATE void _sapp_wgpu_device_lost_cb(const WGPUDevice* dev, WGPUDeviceLostReason reason, WGPUStringView msg, void* ud1, void* ud2) {
  3550. _SOKOL_UNUSED(dev); _SOKOL_UNUSED(reason); _SOKOL_UNUSED(ud1); _SOKOL_UNUSED(ud2);
  3551. // NOTE: on wgpuInstanceRelease(), the device lost callback is always called with
  3552. // WGPUDeviceLostReason_CallbackCancelled (even though no device should exist at that point)
  3553. if (reason != WGPUDeviceLostReason_CallbackCancelled) {
  3554. SOKOL_ASSERT(msg.data && (msg.length > 0));
  3555. char buf[1024];
  3556. _sapp_strcpy_range(msg.data, msg.length, buf, sizeof(buf));
  3557. _SAPP_ERROR_MSG(WGPU_DEVICE_LOST, buf);
  3558. }
  3559. }
  3560. // NOTE: emdawnwebgpu doesn't seem to have a device logging callback
  3561. #if !defined(_SAPP_EMSCRIPTEN)
  3562. _SOKOL_PRIVATE void _sapp_wgpu_device_logging_cb(WGPULoggingType log_type, WGPUStringView msg, void* ud1, void* ud2) {
  3563. _SOKOL_UNUSED(log_type); _SOKOL_UNUSED(ud1); _SOKOL_UNUSED(ud2);
  3564. SOKOL_ASSERT(msg.data && (msg.length > 0));
  3565. char buf[1024];
  3566. _sapp_strcpy_range(msg.data, msg.length, buf, sizeof(buf));
  3567. switch (log_type) {
  3568. case WGPULoggingType_Warning:
  3569. _SAPP_WARN_MSG(WGPU_DEVICE_LOG, buf);
  3570. break;
  3571. case WGPULoggingType_Error:
  3572. _SAPP_ERROR_MSG(WGPU_DEVICE_LOG, buf);
  3573. break;
  3574. default:
  3575. _SAPP_INFO_MSG(WGPU_DEVICE_LOG, buf);
  3576. break;
  3577. }
  3578. }
  3579. #endif
  3580. _SOKOL_PRIVATE void _sapp_wgpu_uncaptured_error_cb(const WGPUDevice* dev, WGPUErrorType err_type, WGPUStringView msg, void* ud1, void* ud2) {
  3581. _SOKOL_UNUSED(dev); _SOKOL_UNUSED(ud1); _SOKOL_UNUSED(ud2);
  3582. if (err_type != WGPUErrorType_NoError) {
  3583. SOKOL_ASSERT(msg.data && (msg.length > 0));
  3584. char buf[1024];
  3585. _sapp_strcpy_range(msg.data, msg.length, buf, sizeof(buf));
  3586. _SAPP_ERROR_MSG(WGPU_DEVICE_UNCAPTURED_ERROR, buf);
  3587. }
  3588. }
  3589. _SOKOL_PRIVATE void _sapp_wgpu_request_device_cb(WGPURequestDeviceStatus status, WGPUDevice device, WGPUStringView msg, void* userdata1, void* userdata2) {
  3590. _SOKOL_UNUSED(msg);
  3591. _SOKOL_UNUSED(userdata1);
  3592. _SOKOL_UNUSED(userdata2);
  3593. SOKOL_ASSERT(!_sapp.wgpu.init_done);
  3594. if (status != WGPURequestDeviceStatus_Success) {
  3595. if (status == WGPURequestDeviceStatus_Error) {
  3596. _SAPP_PANIC(WGPU_REQUEST_DEVICE_STATUS_ERROR);
  3597. } else {
  3598. _SAPP_PANIC(WGPU_REQUEST_DEVICE_STATUS_UNKNOWN);
  3599. }
  3600. }
  3601. SOKOL_ASSERT(device);
  3602. _sapp.wgpu.device = device;
  3603. #if !defined(_SAPP_EMSCRIPTEN)
  3604. _SAPP_STRUCT(WGPULoggingCallbackInfo, cb_info);
  3605. cb_info.callback = _sapp_wgpu_device_logging_cb;
  3606. wgpuDeviceSetLoggingCallback(_sapp.wgpu.device, cb_info);
  3607. #endif
  3608. _sapp_wgpu_create_swapchain(false);
  3609. _sapp.wgpu.init_done = true;
  3610. }
  3611. _SOKOL_PRIVATE void _sapp_wgpu_create_device_and_swapchain(void) {
  3612. SOKOL_ASSERT(_sapp.wgpu.adapter);
  3613. size_t cur_feature_index = 1;
  3614. #define _SAPP_WGPU_MAX_REQUESTED_FEATURES (8)
  3615. WGPUFeatureName requiredFeatures[_SAPP_WGPU_MAX_REQUESTED_FEATURES] = {
  3616. WGPUFeatureName_Depth32FloatStencil8,
  3617. };
  3618. // check for optional features we're interested in
  3619. if (wgpuAdapterHasFeature(_sapp.wgpu.adapter, WGPUFeatureName_TextureCompressionBC)) {
  3620. SOKOL_ASSERT(cur_feature_index < _SAPP_WGPU_MAX_REQUESTED_FEATURES);
  3621. requiredFeatures[cur_feature_index++] = WGPUFeatureName_TextureCompressionBC;
  3622. }
  3623. if (wgpuAdapterHasFeature(_sapp.wgpu.adapter, WGPUFeatureName_TextureCompressionETC2)) {
  3624. SOKOL_ASSERT(cur_feature_index < _SAPP_WGPU_MAX_REQUESTED_FEATURES);
  3625. requiredFeatures[cur_feature_index++] = WGPUFeatureName_TextureCompressionETC2;
  3626. }
  3627. if (wgpuAdapterHasFeature(_sapp.wgpu.adapter, WGPUFeatureName_TextureCompressionASTC)) {
  3628. SOKOL_ASSERT(cur_feature_index < _SAPP_WGPU_MAX_REQUESTED_FEATURES);
  3629. requiredFeatures[cur_feature_index++] = WGPUFeatureName_TextureCompressionASTC;
  3630. }
  3631. if (wgpuAdapterHasFeature(_sapp.wgpu.adapter, WGPUFeatureName_Float32Filterable)) {
  3632. SOKOL_ASSERT(cur_feature_index < _SAPP_WGPU_MAX_REQUESTED_FEATURES);
  3633. requiredFeatures[cur_feature_index++] = WGPUFeatureName_Float32Filterable;
  3634. }
  3635. #undef _SAPP_WGPU_MAX_REQUESTED_FEATURES
  3636. WGPULimits adapterLimits = WGPU_LIMITS_INIT;
  3637. wgpuAdapterGetLimits(_sapp.wgpu.adapter, &adapterLimits);
  3638. WGPULimits requiredLimits = WGPU_LIMITS_INIT;
  3639. requiredLimits.maxColorAttachments = adapterLimits.maxColorAttachments;
  3640. requiredLimits.maxSampledTexturesPerShaderStage = adapterLimits.maxSampledTexturesPerShaderStage;
  3641. requiredLimits.maxStorageBuffersPerShaderStage = adapterLimits.maxStorageBuffersPerShaderStage;
  3642. requiredLimits.maxStorageTexturesPerShaderStage = adapterLimits.maxStorageTexturesPerShaderStage;
  3643. _SAPP_STRUCT(WGPURequestDeviceCallbackInfo, cb_info);
  3644. cb_info.mode = _sapp_wgpu_callbackmode();
  3645. cb_info.callback = _sapp_wgpu_request_device_cb;
  3646. _SAPP_STRUCT(WGPUDeviceDescriptor, dev_desc);
  3647. dev_desc.requiredFeatureCount = cur_feature_index;
  3648. dev_desc.requiredFeatures = requiredFeatures;
  3649. dev_desc.requiredLimits = &requiredLimits;
  3650. dev_desc.deviceLostCallbackInfo.mode = WGPUCallbackMode_AllowProcessEvents;
  3651. dev_desc.deviceLostCallbackInfo.callback = _sapp_wgpu_device_lost_cb;
  3652. dev_desc.uncapturedErrorCallbackInfo.callback = _sapp_wgpu_uncaptured_error_cb;
  3653. WGPUFuture future = wgpuAdapterRequestDevice(_sapp.wgpu.adapter, &dev_desc, cb_info);
  3654. #if defined(_SAPP_WGPU_HAS_WAIT)
  3655. _sapp_wgpu_await(future);
  3656. #else
  3657. _SOKOL_UNUSED(future);
  3658. #endif
  3659. }
  3660. _SOKOL_PRIVATE void _sapp_wgpu_request_adapter_cb(WGPURequestAdapterStatus status, WGPUAdapter adapter, WGPUStringView msg, void* userdata1, void* userdata2) {
  3661. _SOKOL_UNUSED(msg);
  3662. _SOKOL_UNUSED(userdata1);
  3663. _SOKOL_UNUSED(userdata2);
  3664. if (status != WGPURequestAdapterStatus_Success) {
  3665. switch (status) {
  3666. case WGPURequestAdapterStatus_Unavailable: _SAPP_PANIC(WGPU_REQUEST_ADAPTER_STATUS_UNAVAILABLE); break;
  3667. case WGPURequestAdapterStatus_Error: _SAPP_PANIC(WGPU_REQUEST_ADAPTER_STATUS_ERROR); break;
  3668. default: _SAPP_PANIC(WGPU_REQUEST_ADAPTER_STATUS_UNKNOWN); break;
  3669. }
  3670. }
  3671. SOKOL_ASSERT(adapter);
  3672. _sapp.wgpu.adapter = adapter;
  3673. #if !defined(_SAPP_WGPU_HAS_WAIT)
  3674. // chain device creation
  3675. _sapp_wgpu_create_device_and_swapchain();
  3676. #endif
  3677. }
  3678. _SOKOL_PRIVATE void _sapp_wgpu_create_adapter(void) {
  3679. SOKOL_ASSERT(_sapp.wgpu.instance);
  3680. // FIXME: power preference?
  3681. _SAPP_STRUCT(WGPURequestAdapterCallbackInfo, cb_info);
  3682. cb_info.mode = _sapp_wgpu_callbackmode();
  3683. cb_info.callback = _sapp_wgpu_request_adapter_cb;
  3684. WGPUFuture future = wgpuInstanceRequestAdapter(_sapp.wgpu.instance, 0, cb_info);
  3685. #if defined(_SAPP_WGPU_HAS_WAIT)
  3686. _sapp_wgpu_await(future);
  3687. #else
  3688. _SOKOL_UNUSED(future);
  3689. #endif
  3690. }
  3691. _SOKOL_PRIVATE void _sapp_wgpu_init(void) {
  3692. SOKOL_ASSERT(0 == _sapp.wgpu.instance);
  3693. SOKOL_ASSERT(!_sapp.wgpu.init_done);
  3694. _SAPP_STRUCT(WGPUInstanceDescriptor, desc);
  3695. #if defined(_SAPP_WGPU_HAS_WAIT)
  3696. WGPUInstanceFeatureName inst_features[1] = {
  3697. WGPUInstanceFeatureName_TimedWaitAny,
  3698. };
  3699. desc.requiredFeatureCount = 1;
  3700. desc.requiredFeatures = inst_features;
  3701. #endif
  3702. _sapp.wgpu.instance = wgpuCreateInstance(&desc);
  3703. if (0 == _sapp.wgpu.instance) {
  3704. _SAPP_PANIC(WGPU_CREATE_INSTANCE_FAILED);
  3705. }
  3706. // NOTE: on Emscripten, device and swapchain creation are chained in the callacks
  3707. _sapp_wgpu_create_adapter();
  3708. #if defined(_SAPP_WGPU_HAS_WAIT)
  3709. _sapp_wgpu_create_device_and_swapchain();
  3710. SOKOL_ASSERT(_sapp.wgpu.init_done);
  3711. #endif
  3712. }
  3713. _SOKOL_PRIVATE void _sapp_wgpu_discard(void) {
  3714. _sapp_wgpu_discard_swapchain(false);
  3715. if (_sapp.wgpu.device) {
  3716. wgpuDeviceRelease(_sapp.wgpu.device);
  3717. _sapp.wgpu.device = 0;
  3718. }
  3719. if (_sapp.wgpu.adapter) {
  3720. wgpuAdapterRelease(_sapp.wgpu.adapter);
  3721. _sapp.wgpu.adapter = 0;
  3722. }
  3723. if (_sapp.wgpu.instance) {
  3724. wgpuInstanceRelease(_sapp.wgpu.instance);
  3725. _sapp.wgpu.instance = 0;
  3726. }
  3727. }
  3728. _SOKOL_PRIVATE void _sapp_wgpu_frame(void) {
  3729. wgpuInstanceProcessEvents(_sapp.wgpu.instance);
  3730. if (_sapp.wgpu.init_done) {
  3731. _sapp_frame();
  3732. if (_sapp.wgpu.swapchain_view) {
  3733. wgpuTextureViewRelease(_sapp.wgpu.swapchain_view);
  3734. _sapp.wgpu.swapchain_view = 0;
  3735. }
  3736. #if !defined(_SAPP_EMSCRIPTEN)
  3737. wgpuSurfacePresent(_sapp.wgpu.surface);
  3738. #endif
  3739. }
  3740. }
  3741. #endif // SOKOL_WGPU
  3742. // ██ ██ ██ ██ ██ ██ ██ █████ ███ ██
  3743. // ██ ██ ██ ██ ██ ██ ██ ██ ██ ████ ██
  3744. // ██ ██ ██ ██ ██ █████ ███████ ██ ██ ██
  3745. // ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
  3746. // ████ ██████ ███████ ██ ██ ██ ██ ██ ████
  3747. //
  3748. // >>vulkan
  3749. // >>vk
  3750. #if defined(SOKOL_VULKAN)
  3751. #if defined(__cplusplus)
  3752. #define _SAPP_VK_ZERO_COUNT_AND_ARRAY(num, type, count_name, array_name) uint32_t count_name = 0; type array_name[num] = {}
  3753. #define _SAPP_VK_MAX_COUNT_AND_ARRAY(num, type, count_name, array_name) uint32_t count_name = num; type array_name[num] = {}
  3754. #else
  3755. #define _SAPP_VK_ZERO_COUNT_AND_ARRAY(num, type, count_name, array_name) uint32_t count_name = 0; type array_name[num] = {0}
  3756. #define _SAPP_VK_MAX_COUNT_AND_ARRAY(num, type, count_name, array_name) uint32_t count_name = num; type array_name[num] = {0}
  3757. #endif
  3758. _SOKOL_PRIVATE int _sapp_vk_mem_find_memory_type_index(uint32_t type_filter, VkMemoryPropertyFlags props) {
  3759. SOKOL_ASSERT(_sapp.vk.physical_device);
  3760. _SAPP_STRUCT(VkPhysicalDeviceMemoryProperties, mem_props);
  3761. vkGetPhysicalDeviceMemoryProperties(_sapp.vk.physical_device, &mem_props);
  3762. for (uint32_t i = 0; i < mem_props.memoryTypeCount; i++) {
  3763. if ((type_filter & (1 << i)) && ((mem_props.memoryTypes[i].propertyFlags & props) == props)) {
  3764. return (int)i;
  3765. }
  3766. }
  3767. return -1;
  3768. }
  3769. _SOKOL_PRIVATE void _sapp_vk_create_instance(void) {
  3770. SOKOL_ASSERT(0 == _sapp.vk.instance);
  3771. _SAPP_STRUCT(VkApplicationInfo, app_info);
  3772. app_info.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
  3773. app_info.pApplicationName = "sokol-app"; // FIXME: override via sapp_desc?
  3774. app_info.applicationVersion = VK_MAKE_VERSION(1, 0, 0);
  3775. app_info.pEngineName = "sokol";
  3776. app_info.engineVersion = VK_MAKE_VERSION(1, 0, 0);
  3777. app_info.apiVersion = VK_API_VERSION_1_3;
  3778. _SAPP_VK_ZERO_COUNT_AND_ARRAY(32, const char*, layer_count, layer_names);
  3779. #if defined(SOKOL_DEBUG)
  3780. layer_names[layer_count++] = "VK_LAYER_KHRONOS_validation";
  3781. SOKOL_ASSERT(layer_count <= 32);
  3782. #endif
  3783. _SAPP_VK_ZERO_COUNT_AND_ARRAY(32, const char*, ext_count, ext_names);
  3784. ext_names[ext_count++] = VK_KHR_SURFACE_EXTENSION_NAME;
  3785. #if defined(VK_USE_PLATFORM_XLIB_KHR)
  3786. ext_names[ext_count++] = VK_KHR_XLIB_SURFACE_EXTENSION_NAME;
  3787. #endif
  3788. SOKOL_ASSERT(ext_count <= 32);
  3789. _SAPP_STRUCT(VkInstanceCreateInfo, create_info);
  3790. create_info.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
  3791. create_info.flags = 0;
  3792. create_info.pApplicationInfo = &app_info;
  3793. create_info.enabledLayerCount = layer_count;
  3794. create_info.ppEnabledLayerNames = layer_names;
  3795. create_info.enabledExtensionCount = ext_count;
  3796. create_info.ppEnabledExtensionNames = ext_names;
  3797. VkResult res = vkCreateInstance(&create_info, 0, &_sapp.vk.instance);
  3798. if (res != VK_SUCCESS) {
  3799. _SAPP_PANIC(VULKAN_CREATE_INSTANCE_FAILED);
  3800. }
  3801. SOKOL_ASSERT(_sapp.vk.instance);
  3802. }
  3803. _SOKOL_PRIVATE void _sapp_vk_destroy_instance(void) {
  3804. SOKOL_ASSERT(_sapp.vk.instance);
  3805. vkDestroyInstance(_sapp.vk.instance, 0);
  3806. _sapp.vk.instance = 0;
  3807. }
  3808. _SOKOL_PRIVATE uint32_t _sapp_vk_required_device_extensions(const char** out_names, uint32_t max_count) {
  3809. SOKOL_ASSERT(out_names && (max_count > 0));
  3810. uint32_t count = 0;
  3811. out_names[count++] = VK_KHR_SWAPCHAIN_EXTENSION_NAME;
  3812. out_names[count++] = VK_EXT_DESCRIPTOR_BUFFER_EXTENSION_NAME;
  3813. SOKOL_ASSERT(count <= max_count); _SOKOL_UNUSED(max_count);
  3814. return count;
  3815. }
  3816. _SOKOL_PRIVATE bool _sapp_vk_check_device_extensions(VkPhysicalDevice pdev, const char** required_exts, uint32_t num_required_exts) {
  3817. SOKOL_ASSERT(pdev && required_exts && num_required_exts > 0);
  3818. uint32_t ext_count = 0;
  3819. VkResult res = vkEnumerateDeviceExtensionProperties(pdev, 0, &ext_count, 0);
  3820. SOKOL_ASSERT(res == VK_SUCCESS); _SOKOL_UNUSED(res);
  3821. if (ext_count == 0) {
  3822. return false;
  3823. }
  3824. VkExtensionProperties* ext_props = (VkExtensionProperties*) _sapp_malloc(sizeof(VkExtensionProperties) * ext_count);
  3825. SOKOL_ASSERT(ext_props);
  3826. res = vkEnumerateDeviceExtensionProperties(pdev, 0, &ext_count, ext_props);
  3827. bool all_supported = true;
  3828. for (uint32_t req_ext_idx = 0; req_ext_idx < num_required_exts; req_ext_idx++) {
  3829. const char* req_ext_name = required_exts[req_ext_idx];
  3830. bool required_ext_supported = false;
  3831. for (uint32_t ext_idx = 0; ext_idx < ext_count; ext_idx++) {
  3832. const VkExtensionProperties* ext_prop = &ext_props[ext_idx];
  3833. if (0 == strcmp(req_ext_name, ext_prop->extensionName)) {
  3834. required_ext_supported = true;
  3835. break;
  3836. }
  3837. }
  3838. if (!required_ext_supported) {
  3839. all_supported = false;
  3840. }
  3841. }
  3842. _sapp_free(ext_props);
  3843. return all_supported;
  3844. }
  3845. _SOKOL_PRIVATE void _sapp_vk_pick_physical_device(void) {
  3846. SOKOL_ASSERT(_sapp.vk.instance);
  3847. SOKOL_ASSERT(_sapp.vk.surface);
  3848. SOKOL_ASSERT(0 == _sapp.vk.physical_device);
  3849. VkResult res = VK_SUCCESS;
  3850. _SAPP_VK_MAX_COUNT_AND_ARRAY(8, VkPhysicalDevice, physical_device_count, physical_devices);
  3851. res = vkEnumeratePhysicalDevices(_sapp.vk.instance, &physical_device_count, physical_devices);
  3852. if ((res != VK_SUCCESS) && (res != VK_INCOMPLETE)) {
  3853. _SAPP_PANIC(VULKAN_ENUMERATE_PHYSICAL_DEVICES_FAILED);
  3854. }
  3855. if (physical_device_count == 0) {
  3856. _SAPP_PANIC(VULKAN_NO_PHYSICAL_DEVICES_FOUND);
  3857. }
  3858. _SAPP_VK_ZERO_COUNT_AND_ARRAY(32, const char*, ext_count, ext_names);
  3859. ext_count = _sapp_vk_required_device_extensions(ext_names, 32);
  3860. VkPhysicalDevice pdev = 0;
  3861. for (uint32_t pdev_idx = 0; pdev_idx < physical_device_count; pdev_idx++) {
  3862. pdev = physical_devices[pdev_idx];
  3863. _SAPP_STRUCT(VkPhysicalDeviceProperties, dev_props);
  3864. vkGetPhysicalDeviceProperties(pdev, &dev_props);
  3865. if (dev_props.apiVersion < VK_API_VERSION_1_3) {
  3866. continue;
  3867. }
  3868. if (!_sapp_vk_check_device_extensions(pdev, ext_names, ext_count)) {
  3869. continue;
  3870. }
  3871. // FIXME: handle theoretical case where graphics and present aren't supported by the same queue family index
  3872. _SAPP_VK_MAX_COUNT_AND_ARRAY(8, VkQueueFamilyProperties, queue_family_props_count, queue_family_props);
  3873. vkGetPhysicalDeviceQueueFamilyProperties(pdev, &queue_family_props_count, queue_family_props);
  3874. bool has_required_queues = false;
  3875. const VkQueueFlags required_flags = VK_QUEUE_GRAPHICS_BIT | VK_QUEUE_COMPUTE_BIT | VK_QUEUE_TRANSFER_BIT;
  3876. for (uint32_t qfp_idx = 0; qfp_idx < queue_family_props_count; qfp_idx++) {
  3877. const VkQueueFlags queue_flags = queue_family_props[qfp_idx].queueFlags;
  3878. if ((queue_flags & required_flags) == required_flags) {
  3879. _sapp.vk.queue_family_index = qfp_idx;
  3880. has_required_queues = true;
  3881. break;
  3882. }
  3883. }
  3884. if (!has_required_queues) {
  3885. continue;
  3886. }
  3887. VkBool32 presentation_supported = false;
  3888. res = vkGetPhysicalDeviceSurfaceSupportKHR(pdev, _sapp.vk.queue_family_index, _sapp.vk.surface, &presentation_supported);
  3889. SOKOL_ASSERT(VK_SUCCESS == res);
  3890. if (!presentation_supported) {
  3891. continue;
  3892. }
  3893. // if we arrive here, found a suitable device
  3894. break;
  3895. }
  3896. if (0 == pdev) {
  3897. _SAPP_PANIC(VULKAN_NO_SUITABLE_PHYSICAL_DEVICE_FOUND);
  3898. }
  3899. _sapp.vk.physical_device = pdev;
  3900. SOKOL_ASSERT(_sapp.vk.physical_device);
  3901. }
  3902. _SOKOL_PRIVATE void _sapp_vk_create_device(void) {
  3903. SOKOL_ASSERT(_sapp.vk.physical_device);
  3904. SOKOL_ASSERT(0 == _sapp.vk.device);
  3905. const float queue_priority = 0.0f;
  3906. _SAPP_STRUCT(VkDeviceQueueCreateInfo, queue_create_info);
  3907. queue_create_info.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
  3908. queue_create_info.queueFamilyIndex = _sapp.vk.queue_family_index;
  3909. queue_create_info.queueCount = 1;
  3910. queue_create_info.pQueuePriorities = &queue_priority;
  3911. _SAPP_VK_ZERO_COUNT_AND_ARRAY(32, const char*, ext_count, ext_names);
  3912. ext_count = _sapp_vk_required_device_extensions(ext_names, 32);
  3913. _SAPP_STRUCT(VkPhysicalDeviceFeatures2, supports);
  3914. supports.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2;
  3915. vkGetPhysicalDeviceFeatures2(_sapp.vk.physical_device, &supports);
  3916. _SAPP_STRUCT(VkPhysicalDeviceDescriptorBufferFeaturesEXT, descriptor_buffer_features);
  3917. descriptor_buffer_features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DESCRIPTOR_BUFFER_FEATURES_EXT;
  3918. descriptor_buffer_features.descriptorBuffer = VK_TRUE;
  3919. _SAPP_STRUCT(VkPhysicalDeviceExtendedDynamicStateFeaturesEXT, xds_features);
  3920. xds_features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTENDED_DYNAMIC_STATE_FEATURES_EXT;
  3921. xds_features.pNext = &descriptor_buffer_features;
  3922. xds_features.extendedDynamicState = VK_TRUE;
  3923. _SAPP_STRUCT(VkPhysicalDeviceVulkan12Features, vk12_features);
  3924. vk12_features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_FEATURES;
  3925. vk12_features.pNext = &xds_features;
  3926. vk12_features.bufferDeviceAddress = VK_TRUE;
  3927. _SAPP_STRUCT(VkPhysicalDeviceVulkan13Features, vk13_features);
  3928. vk13_features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_3_FEATURES;
  3929. vk13_features.pNext = &vk12_features;
  3930. vk13_features.dynamicRendering = VK_TRUE;
  3931. vk13_features.synchronization2 = VK_TRUE;
  3932. _SAPP_STRUCT(VkPhysicalDeviceFeatures2, required);
  3933. required.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2;
  3934. required.pNext = &vk13_features;
  3935. required.features.samplerAnisotropy = VK_TRUE;
  3936. if (supports.features.textureCompressionBC) {
  3937. required.features.textureCompressionBC = VK_TRUE;
  3938. }
  3939. if (supports.features.textureCompressionETC2) {
  3940. required.features.textureCompressionETC2 = VK_TRUE;
  3941. }
  3942. if (supports.features.textureCompressionASTC_LDR) {
  3943. required.features.textureCompressionASTC_LDR = VK_TRUE;
  3944. }
  3945. _SAPP_STRUCT(VkDeviceCreateInfo, dev_create_info);
  3946. dev_create_info.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
  3947. dev_create_info.pNext = &required;
  3948. dev_create_info.queueCreateInfoCount = 1;
  3949. dev_create_info.pQueueCreateInfos = &queue_create_info;
  3950. dev_create_info.enabledExtensionCount = ext_count;
  3951. dev_create_info.ppEnabledExtensionNames = ext_names;
  3952. VkResult res = vkCreateDevice(_sapp.vk.physical_device, &dev_create_info, 0, &_sapp.vk.device);
  3953. if (res != VK_SUCCESS) {
  3954. switch (res) {
  3955. case VK_ERROR_EXTENSION_NOT_PRESENT:
  3956. _SAPP_PANIC(VULKAN_CREATE_DEVICE_FAILED_EXTENSION_NOT_PRESENT);
  3957. break;
  3958. case VK_ERROR_FEATURE_NOT_PRESENT:
  3959. _SAPP_PANIC(VULKAN_CREATE_DEVICE_FAILED_FEATURE_NOT_PRESENT);
  3960. break;
  3961. case VK_ERROR_INITIALIZATION_FAILED:
  3962. _SAPP_PANIC(VULKAN_CREATE_DEVICE_FAILED_INITIALIZATION_FAILED);
  3963. break;
  3964. default:
  3965. _SAPP_PANIC(VULKAN_CREATE_DEVICE_FAILED_OTHER);
  3966. break;
  3967. }
  3968. }
  3969. SOKOL_ASSERT(_sapp.vk.device);
  3970. SOKOL_ASSERT(0 == _sapp.vk.queue);
  3971. vkGetDeviceQueue(_sapp.vk.device, _sapp.vk.queue_family_index, 0, &_sapp.vk.queue);
  3972. SOKOL_ASSERT(_sapp.vk.queue);
  3973. }
  3974. _SOKOL_PRIVATE void _sapp_vk_destroy_device(void) {
  3975. SOKOL_ASSERT(_sapp.vk.device);
  3976. vkDestroyDevice(_sapp.vk.device, 0);
  3977. _sapp.vk.device = 0;
  3978. _sapp.vk.queue = 0;
  3979. }
  3980. _SOKOL_PRIVATE void _sapp_vk_create_surface(void) {
  3981. SOKOL_ASSERT(_sapp.vk.instance);
  3982. SOKOL_ASSERT(0 == _sapp.vk.surface);
  3983. VkResult res = VK_SUCCESS;
  3984. #if defined(_SAPP_LINUX)
  3985. _SAPP_STRUCT(VkXlibSurfaceCreateInfoKHR, xlib_info);
  3986. xlib_info.sType = VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR;
  3987. xlib_info.dpy = _sapp.x11.display;
  3988. xlib_info.window = _sapp.x11.window;
  3989. res = vkCreateXlibSurfaceKHR(_sapp.vk.instance, &xlib_info, 0, &_sapp.vk.surface);
  3990. #else
  3991. #error "sokol_app.h: unsupported Vulkan platform"
  3992. #endif
  3993. if (res != VK_SUCCESS) {
  3994. _SAPP_PANIC(VULKAN_CREATE_SURFACE_FAILED);
  3995. }
  3996. SOKOL_ASSERT(_sapp.vk.surface);
  3997. }
  3998. _SOKOL_PRIVATE void _sapp_vk_destroy_surface(void) {
  3999. SOKOL_ASSERT(_sapp.vk.instance);
  4000. SOKOL_ASSERT(_sapp.vk.surface);
  4001. vkDestroySurfaceKHR(_sapp.vk.instance, _sapp.vk.surface, 0);
  4002. _sapp.vk.surface = 0;
  4003. }
  4004. _SOKOL_PRIVATE VkSurfaceFormatKHR _sapp_vk_pick_surface_format(void) {
  4005. SOKOL_ASSERT(_sapp.vk.instance);
  4006. SOKOL_ASSERT(_sapp.vk.surface);
  4007. _SAPP_VK_MAX_COUNT_AND_ARRAY(64, VkSurfaceFormatKHR, fmt_count, formats);
  4008. VkResult res = vkGetPhysicalDeviceSurfaceFormatsKHR(_sapp.vk.physical_device, _sapp.vk.surface, &fmt_count, formats);
  4009. SOKOL_ASSERT((res == VK_SUCCESS) || (res == VK_INCOMPLETE)); _SOKOL_UNUSED(res);
  4010. SOKOL_ASSERT(fmt_count > 0);
  4011. // FIXME: only accept non-SRGB formats until sokol_app.h gets proper SRGB support
  4012. for (uint32_t i = 0; i < fmt_count; i++) {
  4013. switch (formats[i].format) {
  4014. case VK_FORMAT_B8G8R8A8_UNORM:
  4015. case VK_FORMAT_R8G8B8A8_UNORM:
  4016. return formats[i];
  4017. default: break;
  4018. }
  4019. }
  4020. // FIXME: fallback might still return an SRGB format
  4021. return formats[0];
  4022. }
  4023. _SOKOL_PRIVATE void _sapp_vk_create_sync_objects(void) {
  4024. SOKOL_ASSERT(_sapp.vk.device);
  4025. _SAPP_STRUCT(VkSemaphoreCreateInfo, create_info);
  4026. create_info.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
  4027. VkResult res;
  4028. _SOKOL_UNUSED(res);
  4029. for (uint32_t i = 0; i < _sapp.vk.num_swapchain_images; i++) {
  4030. SOKOL_ASSERT(0 == _sapp.vk.sync[i].present_complete_sem);
  4031. SOKOL_ASSERT(0 == _sapp.vk.sync[i].render_finished_sem);
  4032. res = vkCreateSemaphore(_sapp.vk.device, &create_info, 0, &_sapp.vk.sync[i].present_complete_sem);
  4033. SOKOL_ASSERT((res == VK_SUCCESS) && (_sapp.vk.sync[i].present_complete_sem));
  4034. res = vkCreateSemaphore(_sapp.vk.device, &create_info, 0, &_sapp.vk.sync[i].render_finished_sem);
  4035. SOKOL_ASSERT((res == VK_SUCCESS) && (_sapp.vk.sync[i].render_finished_sem));
  4036. }
  4037. }
  4038. _SOKOL_PRIVATE void _sapp_vk_destroy_sync_objects(void) {
  4039. SOKOL_ASSERT(_sapp.vk.device);
  4040. for (uint32_t i = 0; i < _sapp.vk.num_swapchain_images; i++) {
  4041. SOKOL_ASSERT(_sapp.vk.sync[i].present_complete_sem);
  4042. SOKOL_ASSERT(_sapp.vk.sync[i].render_finished_sem);
  4043. vkDestroySemaphore(_sapp.vk.device, _sapp.vk.sync[i].present_complete_sem, 0);
  4044. vkDestroySemaphore(_sapp.vk.device, _sapp.vk.sync[i].render_finished_sem, 0);
  4045. _sapp.vk.sync[i].present_complete_sem = 0;
  4046. _sapp.vk.sync[i].render_finished_sem = 0;
  4047. }
  4048. }
  4049. _SOKOL_PRIVATE VkDeviceMemory _sapp_vk_mem_alloc_image_memory(const VkMemoryRequirements* mem_reqs) {
  4050. SOKOL_ASSERT(_sapp.vk.device);
  4051. SOKOL_ASSERT(mem_reqs);
  4052. int mem_type_index = _sapp_vk_mem_find_memory_type_index(mem_reqs->memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
  4053. if (-1 == mem_type_index) {
  4054. _SAPP_ERROR(VULKAN_ALLOC_DEVICE_MEMORY_NO_SUITABLE_MEMORY_TYPE);
  4055. return 0;
  4056. }
  4057. _SAPP_STRUCT(VkMemoryAllocateInfo, alloc_info);
  4058. alloc_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
  4059. alloc_info.allocationSize = mem_reqs->size;
  4060. alloc_info.memoryTypeIndex = (uint32_t) mem_type_index;
  4061. VkDeviceMemory vk_dev_mem = 0;
  4062. VkResult res = vkAllocateMemory(_sapp.vk.device, &alloc_info, 0, &vk_dev_mem);
  4063. if (res != VK_SUCCESS) {
  4064. _SAPP_ERROR(VULKAN_ALLOCATE_MEMORY_FAILED);
  4065. return 0;
  4066. }
  4067. SOKOL_ASSERT(vk_dev_mem);
  4068. return vk_dev_mem;
  4069. }
  4070. _SOKOL_PRIVATE void _sapp_vk_mem_free_image_memory(VkDeviceMemory vk_dev_mem) {
  4071. SOKOL_ASSERT(_sapp.vk.device);
  4072. SOKOL_ASSERT(vk_dev_mem);
  4073. vkFreeMemory(_sapp.vk.device, vk_dev_mem, 0);
  4074. }
  4075. _SOKOL_PRIVATE void _sapp_vk_swapchain_destroy_surface(_sapp_vk_swapchain_surface_t* surf) {
  4076. SOKOL_ASSERT(surf);
  4077. SOKOL_ASSERT(surf->img);
  4078. SOKOL_ASSERT(surf->mem);
  4079. SOKOL_ASSERT(surf->view);
  4080. vkDestroyImageView(_sapp.vk.device, surf->view, 0);
  4081. surf->view = 0;
  4082. _sapp_vk_mem_free_image_memory(surf->mem);
  4083. surf->mem = 0;
  4084. vkDestroyImage(_sapp.vk.device, surf->img, 0);
  4085. surf->img = 0;
  4086. }
  4087. _SOKOL_PRIVATE void _sapp_vk_swapchain_create_surface(
  4088. _sapp_vk_swapchain_surface_t* surf,
  4089. bool recreate,
  4090. VkFormat format,
  4091. uint32_t width,
  4092. uint32_t height,
  4093. VkSampleCountFlagBits sample_count_flags,
  4094. VkImageUsageFlags usage,
  4095. VkImageAspectFlags aspect_mask)
  4096. {
  4097. SOKOL_ASSERT(_sapp.vk.physical_device);
  4098. SOKOL_ASSERT(_sapp.vk.device);
  4099. SOKOL_ASSERT(surf);
  4100. if (recreate) {
  4101. _sapp_vk_swapchain_destroy_surface(surf);
  4102. }
  4103. SOKOL_ASSERT(0 == surf->img);
  4104. SOKOL_ASSERT(0 == surf->mem);
  4105. SOKOL_ASSERT(0 == surf->view);
  4106. VkResult res;
  4107. _SAPP_STRUCT(VkImageCreateInfo, img_create_info);
  4108. img_create_info.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
  4109. img_create_info.imageType = VK_IMAGE_TYPE_2D;
  4110. img_create_info.format = format;
  4111. img_create_info.extent.width = width;
  4112. img_create_info.extent.height = height;
  4113. img_create_info.extent.depth = 1;
  4114. img_create_info.mipLevels = 1;
  4115. img_create_info.arrayLayers = 1;
  4116. img_create_info.samples = sample_count_flags;
  4117. img_create_info.tiling = VK_IMAGE_TILING_OPTIMAL;
  4118. img_create_info.usage = usage;
  4119. img_create_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
  4120. img_create_info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
  4121. res = vkCreateImage(_sapp.vk.device, &img_create_info, 0, &surf->img);
  4122. if (res != VK_SUCCESS) {
  4123. _SAPP_PANIC(VULKAN_SWAPCHAIN_CREATE_IMAGE_FAILED);
  4124. }
  4125. SOKOL_ASSERT(surf->img);
  4126. _SAPP_STRUCT(VkMemoryRequirements, mem_reqs);
  4127. vkGetImageMemoryRequirements(_sapp.vk.device, surf->img, &mem_reqs);
  4128. surf->mem = _sapp_vk_mem_alloc_image_memory(&mem_reqs);
  4129. if (0 == surf->mem) {
  4130. _SAPP_PANIC(VULKAN_SWAPCHAIN_ALLOC_IMAGE_DEVICE_MEMORY_FAILED);
  4131. }
  4132. res = vkBindImageMemory(_sapp.vk.device, surf->img, surf->mem, 0);
  4133. if (res != VK_SUCCESS) {
  4134. _SAPP_PANIC(VULKAN_SWAPCHAIN_BIND_IMAGE_MEMORY_FAILED);
  4135. }
  4136. SOKOL_ASSERT(surf->mem);
  4137. _SAPP_STRUCT(VkImageViewCreateInfo, view_create_info);
  4138. view_create_info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
  4139. view_create_info.image = surf->img;
  4140. view_create_info.viewType = VK_IMAGE_VIEW_TYPE_2D;
  4141. view_create_info.format = format;
  4142. view_create_info.subresourceRange.aspectMask = aspect_mask;
  4143. view_create_info.subresourceRange.levelCount = 1;
  4144. view_create_info.subresourceRange.layerCount = 1;
  4145. res = vkCreateImageView(_sapp.vk.device, &view_create_info, 0, &surf->view);
  4146. if (res != VK_SUCCESS) {
  4147. _SAPP_PANIC(VULKAN_SWAPCHAIN_CREATE_IMAGE_VIEW_FAILED);
  4148. }
  4149. SOKOL_ASSERT(surf->view);
  4150. }
  4151. _SOKOL_PRIVATE void _sapp_vk_create_swapchain(bool recreate) {
  4152. SOKOL_ASSERT(_sapp.vk.physical_device);
  4153. SOKOL_ASSERT(_sapp.vk.surface);
  4154. SOKOL_ASSERT(_sapp.vk.device);
  4155. if (!recreate) {
  4156. SOKOL_ASSERT(0 == _sapp.vk.swapchain);
  4157. SOKOL_ASSERT(0 == _sapp.vk.num_swapchain_images);
  4158. SOKOL_ASSERT(0 == _sapp.vk.swapchain_images[0]);
  4159. SOKOL_ASSERT(0 == _sapp.vk.swapchain_views[0]);
  4160. } else {
  4161. SOKOL_ASSERT(_sapp.vk.swapchain);
  4162. SOKOL_ASSERT(_sapp.vk.num_swapchain_images > 0);
  4163. SOKOL_ASSERT(_sapp.vk.swapchain_images[0]);
  4164. SOKOL_ASSERT(_sapp.vk.swapchain_views[0]);
  4165. }
  4166. VkSwapchainKHR old_swapchain = _sapp.vk.swapchain;
  4167. _SAPP_STRUCT(VkSurfaceCapabilitiesKHR, surf_caps);
  4168. VkResult res = vkGetPhysicalDeviceSurfaceCapabilitiesKHR(_sapp.vk.physical_device, _sapp.vk.surface, &surf_caps);
  4169. SOKOL_ASSERT(res == VK_SUCCESS);
  4170. const uint32_t width = surf_caps.currentExtent.width;
  4171. const uint32_t height = surf_caps.currentExtent.height;
  4172. _sapp.vk.surface_format = _sapp_vk_pick_surface_format();
  4173. // FIXME: pick better present-mode if supported
  4174. VkPresentModeKHR present_mode = VK_PRESENT_MODE_FIFO_KHR;
  4175. // FIXME: better imageExtent (scale vs no-scale!)
  4176. _SAPP_STRUCT(VkSwapchainCreateInfoKHR, create_info);
  4177. create_info.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;
  4178. create_info.flags = 0; // FIXME?
  4179. create_info.surface = _sapp.vk.surface;
  4180. create_info.minImageCount = surf_caps.minImageCount;
  4181. create_info.imageFormat = _sapp.vk.surface_format.format;
  4182. create_info.imageColorSpace = _sapp.vk.surface_format.colorSpace;
  4183. create_info.imageExtent.width = width;
  4184. create_info.imageExtent.height = height;
  4185. create_info.imageArrayLayers = 1;
  4186. create_info.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
  4187. create_info.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
  4188. create_info.preTransform = surf_caps.currentTransform;
  4189. create_info.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
  4190. create_info.presentMode = present_mode;
  4191. create_info.clipped = true;
  4192. create_info.oldSwapchain = old_swapchain;
  4193. res = vkCreateSwapchainKHR(_sapp.vk.device, &create_info, 0, &_sapp.vk.swapchain);
  4194. if (res != VK_SUCCESS) {
  4195. _SAPP_PANIC(VULKAN_CREATE_SWAPCHAIN_FAILED);
  4196. }
  4197. SOKOL_ASSERT(_sapp.vk.swapchain);
  4198. if (old_swapchain) {
  4199. // NOTE: destroying the depth- and msaa-surfaces happens
  4200. // down in the respective _sapp_vk_swapchain_create_surface() calls!
  4201. for (uint32_t i = 0; i < _sapp.vk.num_swapchain_images; i++) {
  4202. SOKOL_ASSERT(_sapp.vk.swapchain_views[i]);
  4203. vkDestroyImageView(_sapp.vk.device, _sapp.vk.swapchain_views[i], 0);
  4204. _sapp.vk.swapchain_views[i] = 0;
  4205. }
  4206. vkDestroySwapchainKHR(_sapp.vk.device, old_swapchain, 0);
  4207. }
  4208. _sapp.vk.num_swapchain_images = _SAPP_VK_MAX_SWAPCHAIN_IMAGES;
  4209. res = vkGetSwapchainImagesKHR(_sapp.vk.device,
  4210. _sapp.vk.swapchain,
  4211. &_sapp.vk.num_swapchain_images,
  4212. _sapp.vk.swapchain_images);
  4213. SOKOL_ASSERT(res == VK_SUCCESS);
  4214. SOKOL_ASSERT(_sapp.vk.num_swapchain_images >= surf_caps.minImageCount);
  4215. _SAPP_STRUCT(VkImageViewCreateInfo, view_create_info);
  4216. view_create_info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
  4217. view_create_info.viewType = VK_IMAGE_VIEW_TYPE_2D;
  4218. view_create_info.format = _sapp.vk.surface_format.format;
  4219. view_create_info.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
  4220. view_create_info.subresourceRange.levelCount = 1;
  4221. view_create_info.subresourceRange.layerCount = 1;
  4222. for (uint32_t i = 0; i < _sapp.vk.num_swapchain_images; i++) {
  4223. SOKOL_ASSERT(_sapp.vk.swapchain_images[i]);
  4224. SOKOL_ASSERT(0 == _sapp.vk.swapchain_views[i]);
  4225. view_create_info.image = _sapp.vk.swapchain_images[i];
  4226. res = vkCreateImageView(_sapp.vk.device, &view_create_info, 0, &_sapp.vk.swapchain_views[i]);
  4227. if (res != VK_SUCCESS) {
  4228. _SAPP_PANIC(VULKAN_SWAPCHAIN_CREATE_IMAGE_VIEW_FAILED);
  4229. }
  4230. SOKOL_ASSERT(_sapp.vk.swapchain_views[i]);
  4231. }
  4232. // create depth-stencil buffer
  4233. _sapp_vk_swapchain_create_surface(&_sapp.vk.depth,
  4234. recreate,
  4235. VK_FORMAT_D32_SFLOAT_S8_UINT,
  4236. width,
  4237. height,
  4238. (VkSampleCountFlagBits)_sapp.sample_count,
  4239. VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT,
  4240. VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT);
  4241. // optionally create MSAA surface
  4242. if (_sapp.sample_count > 1) {
  4243. _sapp_vk_swapchain_create_surface(&_sapp.vk.msaa,
  4244. recreate,
  4245. _sapp.vk.surface_format.format,
  4246. width,
  4247. height,
  4248. (VkSampleCountFlagBits)_sapp.sample_count,
  4249. VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT,
  4250. VK_IMAGE_ASPECT_COLOR_BIT);
  4251. }
  4252. // this is the only place in the Vulkan code path which updates
  4253. // _sapp.framebuffer_width/height
  4254. _sapp.framebuffer_width = (int)surf_caps.currentExtent.width;
  4255. _sapp.framebuffer_height = (int)surf_caps.currentExtent.height;
  4256. }
  4257. _SOKOL_PRIVATE void _sapp_vk_destroy_swapchain(void) {
  4258. SOKOL_ASSERT(_sapp.vk.device);
  4259. SOKOL_ASSERT(_sapp.vk.swapchain);
  4260. SOKOL_ASSERT(_sapp.vk.num_swapchain_images > 0);
  4261. if (_sapp.vk.msaa.img) {
  4262. _sapp_vk_swapchain_destroy_surface(&_sapp.vk.msaa);
  4263. }
  4264. _sapp_vk_swapchain_destroy_surface(&_sapp.vk.depth);
  4265. for (uint32_t i = 0; i < _sapp.vk.num_swapchain_images; i++) {
  4266. SOKOL_ASSERT(_sapp.vk.swapchain_views[i]);
  4267. vkDestroyImageView(_sapp.vk.device, _sapp.vk.swapchain_views[i], 0);
  4268. _sapp.vk.swapchain_views[i] = 0;
  4269. _sapp.vk.swapchain_images[i] = 0;
  4270. }
  4271. vkDestroySwapchainKHR(_sapp.vk.device, _sapp.vk.swapchain, 0);
  4272. _sapp.vk.swapchain = 0;
  4273. _sapp.vk.num_swapchain_images = 0;
  4274. }
  4275. #if defined(_SAPP_LINUX)
  4276. _SOKOL_PRIVATE void _sapp_x11_app_event(sapp_event_type type);
  4277. #endif
  4278. _SOKOL_PRIVATE void _sapp_vk_recreate_swapchain(void) {
  4279. SOKOL_ASSERT(_sapp.vk.device);
  4280. vkDeviceWaitIdle(_sapp.vk.device);
  4281. int fb_width = _sapp.framebuffer_width;
  4282. int fb_height = _sapp.framebuffer_height;
  4283. _sapp_vk_create_swapchain(true);
  4284. if ((fb_width != _sapp.framebuffer_width) || (fb_height != _sapp.framebuffer_height)) {
  4285. if (!_sapp.first_frame) {
  4286. #if defined(_SAPP_LINUX)
  4287. _sapp_x11_app_event(SAPP_EVENTTYPE_RESIZED);
  4288. #endif
  4289. }
  4290. }
  4291. }
  4292. _SOKOL_PRIVATE void _sapp_vk_init(void) {
  4293. _sapp_vk_create_instance();
  4294. _sapp_vk_create_surface();
  4295. _sapp_vk_pick_physical_device();
  4296. _sapp_vk_create_device();
  4297. _sapp_vk_create_swapchain(false);
  4298. _sapp_vk_create_sync_objects();
  4299. }
  4300. _SOKOL_PRIVATE void _sapp_vk_discard(void) {
  4301. SOKOL_ASSERT(_sapp.vk.device);
  4302. vkDeviceWaitIdle(_sapp.vk.device);
  4303. _sapp_vk_destroy_sync_objects();
  4304. _sapp_vk_destroy_swapchain();
  4305. _sapp_vk_destroy_device();
  4306. _sapp_vk_destroy_surface();
  4307. _sapp_vk_destroy_instance();
  4308. }
  4309. _SOKOL_PRIVATE void _sapp_vk_swapchain_next(void) {
  4310. SOKOL_ASSERT(_sapp.vk.device);
  4311. SOKOL_ASSERT(_sapp.vk.swapchain);
  4312. VkResult res = vkAcquireNextImageKHR(
  4313. _sapp.vk.device,
  4314. _sapp.vk.swapchain,
  4315. UINT64_MAX, // timeout
  4316. _sapp.vk.sync[_sapp.vk.sync_slot].present_complete_sem, // semaphore to signal
  4317. 0, // fence to signal
  4318. &_sapp.vk.cur_swapchain_image_index);
  4319. if ((res != VK_NOT_READY) && (res != VK_SUBOPTIMAL_KHR) && (res != VK_SUCCESS) && (res != VK_TIMEOUT)) {
  4320. _SAPP_WARN(VULKAN_ACQUIRE_NEXT_IMAGE_FAILED);
  4321. }
  4322. }
  4323. _SOKOL_PRIVATE void _sapp_vk_present(void) {
  4324. SOKOL_ASSERT(_sapp.vk.queue);
  4325. _SAPP_STRUCT(VkPresentInfoKHR, present_info);
  4326. present_info.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
  4327. present_info.waitSemaphoreCount = 1;
  4328. present_info.pWaitSemaphores = &_sapp.vk.sync[_sapp.vk.cur_swapchain_image_index].render_finished_sem;
  4329. present_info.swapchainCount = 1;
  4330. present_info.pSwapchains = &_sapp.vk.swapchain;
  4331. present_info.pImageIndices = &_sapp.vk.cur_swapchain_image_index;
  4332. VkResult res = vkQueuePresentKHR(_sapp.vk.queue, &present_info);
  4333. if ((res == VK_ERROR_OUT_OF_DATE_KHR) || (res == VK_SUBOPTIMAL_KHR)) {
  4334. _sapp_vk_recreate_swapchain();
  4335. } else if (res != VK_SUCCESS) {
  4336. _SAPP_WARN(VULKAN_QUEUE_PRESENT_FAILED);
  4337. }
  4338. }
  4339. _SOKOL_PRIVATE void _sapp_vk_frame(void) {
  4340. _sapp_frame();
  4341. _sapp_vk_present();
  4342. _sapp.vk.sync_slot = (_sapp.vk.sync_slot + 1) % _sapp.vk.num_swapchain_images;
  4343. }
  4344. #endif // SOKOL_VULKAN
  4345. // █████ ██████ ██████ ██ ███████
  4346. // ██ ██ ██ ██ ██ ██ ██ ██
  4347. // ███████ ██████ ██████ ██ █████
  4348. // ██ ██ ██ ██ ██ ██
  4349. // ██ ██ ██ ██ ███████ ███████
  4350. //
  4351. // >>apple
  4352. #if defined(_SAPP_APPLE)
  4353. #if __has_feature(objc_arc)
  4354. #define _SAPP_OBJC_RELEASE(obj) { obj = nil; }
  4355. #else
  4356. #define _SAPP_OBJC_RELEASE(obj) { [obj release]; obj = nil; }
  4357. #endif
  4358. // ███ ███ █████ ██████ ██████ ███████
  4359. // ████ ████ ██ ██ ██ ██ ██ ██
  4360. // ██ ████ ██ ███████ ██ ██ ██ ███████
  4361. // ██ ██ ██ ██ ██ ██ ██ ██ ██
  4362. // ██ ██ ██ ██ ██████ ██████ ███████
  4363. //
  4364. // >>macos
  4365. #if defined(_SAPP_MACOS)
  4366. NSInteger _sapp_macos_max_fps(void) {
  4367. NSInteger max_fps = 60;
  4368. #if (__MAC_OS_X_VERSION_MAX_ALLOWED >= 120000)
  4369. if (@available(macOS 12.0, *)) {
  4370. max_fps = [NSScreen.mainScreen maximumFramesPerSecond];
  4371. }
  4372. #endif
  4373. return max_fps;
  4374. }
  4375. #if defined(SOKOL_METAL)
  4376. _SOKOL_PRIVATE void _sapp_macos_mtl_init(void) {
  4377. NSInteger max_fps = _sapp_macos_max_fps();
  4378. // NOTE: when eventually switching to CAMetalLayer, use the specialized
  4379. // CAMetalDisplayLink instead of CADisplayLink!
  4380. _sapp.macos.mtl_device = MTLCreateSystemDefaultDevice();
  4381. _sapp.macos.view = [[_sapp_macos_view alloc] init];
  4382. [_sapp.macos.view updateTrackingAreas];
  4383. _sapp.macos.view.preferredFramesPerSecond = max_fps / _sapp.swap_interval;
  4384. _sapp.macos.view.device = _sapp.macos.mtl_device;
  4385. _sapp.macos.view.colorPixelFormat = MTLPixelFormatBGRA8Unorm;
  4386. _sapp.macos.view.depthStencilPixelFormat = MTLPixelFormatDepth32Float_Stencil8;
  4387. _sapp.macos.view.sampleCount = (NSUInteger) _sapp.sample_count;
  4388. _sapp.macos.view.autoResizeDrawable = false;
  4389. _sapp.macos.view.layer.magnificationFilter = kCAFilterNearest;
  4390. }
  4391. _SOKOL_PRIVATE void _sapp_macos_mtl_discard_state(void) {
  4392. _SAPP_OBJC_RELEASE(_sapp.macos.mtl_device);
  4393. }
  4394. _SOKOL_PRIVATE bool _sapp_macos_mtl_update_framebuffer_dimensions(NSRect view_bounds) {
  4395. _sapp.framebuffer_width = _sapp_roundf_gzero(view_bounds.size.width * _sapp.dpi_scale);
  4396. _sapp.framebuffer_height = _sapp_roundf_gzero(view_bounds.size.height * _sapp.dpi_scale);
  4397. const CGSize cur_fb_size = _sapp.macos.view.drawableSize;
  4398. int cur_fb_width = _sapp_roundf_gzero(cur_fb_size.width);
  4399. int cur_fb_height = _sapp_roundf_gzero(cur_fb_size.height);
  4400. bool dim_changed = (_sapp.framebuffer_width != cur_fb_width) || (_sapp.framebuffer_height != cur_fb_height);
  4401. if (dim_changed) {
  4402. const CGSize drawable_size = { (CGFloat) _sapp.framebuffer_width, (CGFloat) _sapp.framebuffer_height };
  4403. _sapp.macos.view.drawableSize = drawable_size;
  4404. }
  4405. return dim_changed;
  4406. }
  4407. #endif
  4408. #if defined(SOKOL_GLCORE)
  4409. _SOKOL_PRIVATE void _sapp_macos_gl_init(NSRect window_rect) {
  4410. NSOpenGLPixelFormatAttribute attrs[32];
  4411. int i = 0;
  4412. attrs[i++] = NSOpenGLPFAAccelerated;
  4413. attrs[i++] = NSOpenGLPFADoubleBuffer;
  4414. attrs[i++] = NSOpenGLPFAOpenGLProfile;
  4415. const int glVersion = _sapp.desc.gl.major_version * 10 + _sapp.desc.gl.minor_version;
  4416. switch(glVersion) {
  4417. case 10: attrs[i++] = NSOpenGLProfileVersionLegacy; break;
  4418. case 32: attrs[i++] = NSOpenGLProfileVersion3_2Core; break;
  4419. case 41: attrs[i++] = NSOpenGLProfileVersion4_1Core; break;
  4420. default:
  4421. _SAPP_PANIC(MACOS_INVALID_NSOPENGL_PROFILE);
  4422. }
  4423. attrs[i++] = NSOpenGLPFAColorSize; attrs[i++] = 24;
  4424. attrs[i++] = NSOpenGLPFAAlphaSize; attrs[i++] = 8;
  4425. attrs[i++] = NSOpenGLPFADepthSize; attrs[i++] = 24;
  4426. attrs[i++] = NSOpenGLPFAStencilSize; attrs[i++] = 8;
  4427. if (_sapp.sample_count > 1) {
  4428. attrs[i++] = NSOpenGLPFAMultisample;
  4429. attrs[i++] = NSOpenGLPFASampleBuffers; attrs[i++] = 1;
  4430. attrs[i++] = NSOpenGLPFASamples; attrs[i++] = (NSOpenGLPixelFormatAttribute)_sapp.sample_count;
  4431. } else {
  4432. attrs[i++] = NSOpenGLPFASampleBuffers; attrs[i++] = 0;
  4433. }
  4434. attrs[i++] = 0;
  4435. NSOpenGLPixelFormat* glpixelformat_obj = [[NSOpenGLPixelFormat alloc] initWithAttributes:attrs];
  4436. SOKOL_ASSERT(glpixelformat_obj != nil);
  4437. _sapp.macos.view = [[_sapp_macos_view alloc]
  4438. initWithFrame:window_rect
  4439. pixelFormat:glpixelformat_obj];
  4440. _SAPP_OBJC_RELEASE(glpixelformat_obj);
  4441. [_sapp.macos.view updateTrackingAreas];
  4442. if (_sapp.desc.high_dpi) {
  4443. [_sapp.macos.view setWantsBestResolutionOpenGLSurface:YES];
  4444. } else {
  4445. [_sapp.macos.view setWantsBestResolutionOpenGLSurface:NO];
  4446. }
  4447. NSTimer* timer_obj = [NSTimer timerWithTimeInterval:0.001
  4448. target:_sapp.macos.view
  4449. selector:@selector(timerFired:)
  4450. userInfo:nil
  4451. repeats:YES];
  4452. [[NSRunLoop currentRunLoop] addTimer:timer_obj forMode:NSDefaultRunLoopMode];
  4453. timer_obj = nil;
  4454. }
  4455. _SOKOL_PRIVATE void _sapp_macos_gl_discard_state(void) {
  4456. // nothing to do here
  4457. }
  4458. _SOKOL_PRIVATE bool _sapp_macos_gl_update_framebuffer_dimensions(NSRect view_bounds) {
  4459. const int cur_fb_width = _sapp_roundf_gzero(view_bounds.size.width * _sapp.dpi_scale);
  4460. const int cur_fb_height = _sapp_roundf_gzero(view_bounds.size.height * _sapp.dpi_scale);
  4461. const bool dim_changed = (_sapp.framebuffer_width != cur_fb_width) || (_sapp.framebuffer_height != cur_fb_height);
  4462. _sapp.framebuffer_width = cur_fb_width;
  4463. _sapp.framebuffer_height = cur_fb_height;
  4464. return dim_changed;
  4465. }
  4466. #endif
  4467. #if defined(SOKOL_WGPU)
  4468. _SOKOL_PRIVATE void _sapp_macos_wgpu_init(void) {
  4469. NSInteger max_fps = _sapp_macos_max_fps();
  4470. _sapp.macos.wgpu.mtl_layer = [CAMetalLayer layer];
  4471. _sapp.macos.wgpu.mtl_layer.magnificationFilter = kCAFilterNearest;
  4472. _sapp.macos.wgpu.mtl_layer.opaque = true;
  4473. // NOTE: might experiment with this, valid values are 2 or 3 (default: 3), I don't see any difference tbh
  4474. // _sapp.macos.wgpu.mtl_layer.maximumDrawableCount = 2;
  4475. _sapp.macos.view = [[_sapp_macos_view alloc] init];
  4476. [_sapp.macos.view updateTrackingAreas];
  4477. _sapp.macos.view.wantsLayer = YES;
  4478. _sapp.macos.view.layer = _sapp.macos.wgpu.mtl_layer;
  4479. _sapp.macos.wgpu.display_link = [_sapp.macos.view displayLinkWithTarget:_sapp.macos.view selector:@selector(displayLinkFired:)];
  4480. float preferred_fps = max_fps / _sapp.swap_interval;
  4481. CAFrameRateRange frame_rate_range = { preferred_fps, preferred_fps, preferred_fps };
  4482. _sapp.macos.wgpu.display_link.preferredFrameRateRange = frame_rate_range;
  4483. [_sapp.macos.wgpu.display_link addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
  4484. _sapp_wgpu_init();
  4485. }
  4486. _SOKOL_PRIVATE void _sapp_macos_wgpu_discard_state(void) {
  4487. _SAPP_OBJC_RELEASE(_sapp.macos.wgpu.display_link);
  4488. _SAPP_OBJC_RELEASE(_sapp.macos.wgpu.mtl_layer);
  4489. _sapp_wgpu_discard();
  4490. }
  4491. _SOKOL_PRIVATE bool _sapp_macos_wgpu_update_framebuffer_dimensions(NSRect view_bounds) {
  4492. _sapp.framebuffer_width = _sapp_roundf_gzero(view_bounds.size.width * _sapp.dpi_scale);
  4493. _sapp.framebuffer_height = _sapp_roundf_gzero(view_bounds.size.height * _sapp.dpi_scale);
  4494. const CGSize cur_fb_size = _sapp.macos.wgpu.mtl_layer.drawableSize;
  4495. int cur_fb_width = _sapp_roundf_gzero(cur_fb_size.width);
  4496. int cur_fb_height = _sapp_roundf_gzero(cur_fb_size.height);
  4497. bool dim_changed = (_sapp.framebuffer_width != cur_fb_width) || (_sapp.framebuffer_height != cur_fb_height);
  4498. if (dim_changed) {
  4499. const CGSize drawable_size = { (CGFloat) _sapp.framebuffer_width, (CGFloat) _sapp.framebuffer_height };
  4500. _sapp.macos.wgpu.mtl_layer.drawableSize = drawable_size;
  4501. _sapp_wgpu_swapchain_size_changed();
  4502. }
  4503. return dim_changed;
  4504. }
  4505. #endif
  4506. _SOKOL_PRIVATE void _sapp_macos_init_keytable(void) {
  4507. _sapp.keycodes[0x1D] = SAPP_KEYCODE_0;
  4508. _sapp.keycodes[0x12] = SAPP_KEYCODE_1;
  4509. _sapp.keycodes[0x13] = SAPP_KEYCODE_2;
  4510. _sapp.keycodes[0x14] = SAPP_KEYCODE_3;
  4511. _sapp.keycodes[0x15] = SAPP_KEYCODE_4;
  4512. _sapp.keycodes[0x17] = SAPP_KEYCODE_5;
  4513. _sapp.keycodes[0x16] = SAPP_KEYCODE_6;
  4514. _sapp.keycodes[0x1A] = SAPP_KEYCODE_7;
  4515. _sapp.keycodes[0x1C] = SAPP_KEYCODE_8;
  4516. _sapp.keycodes[0x19] = SAPP_KEYCODE_9;
  4517. _sapp.keycodes[0x00] = SAPP_KEYCODE_A;
  4518. _sapp.keycodes[0x0B] = SAPP_KEYCODE_B;
  4519. _sapp.keycodes[0x08] = SAPP_KEYCODE_C;
  4520. _sapp.keycodes[0x02] = SAPP_KEYCODE_D;
  4521. _sapp.keycodes[0x0E] = SAPP_KEYCODE_E;
  4522. _sapp.keycodes[0x03] = SAPP_KEYCODE_F;
  4523. _sapp.keycodes[0x05] = SAPP_KEYCODE_G;
  4524. _sapp.keycodes[0x04] = SAPP_KEYCODE_H;
  4525. _sapp.keycodes[0x22] = SAPP_KEYCODE_I;
  4526. _sapp.keycodes[0x26] = SAPP_KEYCODE_J;
  4527. _sapp.keycodes[0x28] = SAPP_KEYCODE_K;
  4528. _sapp.keycodes[0x25] = SAPP_KEYCODE_L;
  4529. _sapp.keycodes[0x2E] = SAPP_KEYCODE_M;
  4530. _sapp.keycodes[0x2D] = SAPP_KEYCODE_N;
  4531. _sapp.keycodes[0x1F] = SAPP_KEYCODE_O;
  4532. _sapp.keycodes[0x23] = SAPP_KEYCODE_P;
  4533. _sapp.keycodes[0x0C] = SAPP_KEYCODE_Q;
  4534. _sapp.keycodes[0x0F] = SAPP_KEYCODE_R;
  4535. _sapp.keycodes[0x01] = SAPP_KEYCODE_S;
  4536. _sapp.keycodes[0x11] = SAPP_KEYCODE_T;
  4537. _sapp.keycodes[0x20] = SAPP_KEYCODE_U;
  4538. _sapp.keycodes[0x09] = SAPP_KEYCODE_V;
  4539. _sapp.keycodes[0x0D] = SAPP_KEYCODE_W;
  4540. _sapp.keycodes[0x07] = SAPP_KEYCODE_X;
  4541. _sapp.keycodes[0x10] = SAPP_KEYCODE_Y;
  4542. _sapp.keycodes[0x06] = SAPP_KEYCODE_Z;
  4543. _sapp.keycodes[0x27] = SAPP_KEYCODE_APOSTROPHE;
  4544. _sapp.keycodes[0x2A] = SAPP_KEYCODE_BACKSLASH;
  4545. _sapp.keycodes[0x2B] = SAPP_KEYCODE_COMMA;
  4546. _sapp.keycodes[0x18] = SAPP_KEYCODE_EQUAL;
  4547. _sapp.keycodes[0x32] = SAPP_KEYCODE_GRAVE_ACCENT;
  4548. _sapp.keycodes[0x21] = SAPP_KEYCODE_LEFT_BRACKET;
  4549. _sapp.keycodes[0x1B] = SAPP_KEYCODE_MINUS;
  4550. _sapp.keycodes[0x2F] = SAPP_KEYCODE_PERIOD;
  4551. _sapp.keycodes[0x1E] = SAPP_KEYCODE_RIGHT_BRACKET;
  4552. _sapp.keycodes[0x29] = SAPP_KEYCODE_SEMICOLON;
  4553. _sapp.keycodes[0x2C] = SAPP_KEYCODE_SLASH;
  4554. _sapp.keycodes[0x0A] = SAPP_KEYCODE_WORLD_1;
  4555. _sapp.keycodes[0x33] = SAPP_KEYCODE_BACKSPACE;
  4556. _sapp.keycodes[0x39] = SAPP_KEYCODE_CAPS_LOCK;
  4557. _sapp.keycodes[0x75] = SAPP_KEYCODE_DELETE;
  4558. _sapp.keycodes[0x7D] = SAPP_KEYCODE_DOWN;
  4559. _sapp.keycodes[0x77] = SAPP_KEYCODE_END;
  4560. _sapp.keycodes[0x24] = SAPP_KEYCODE_ENTER;
  4561. _sapp.keycodes[0x35] = SAPP_KEYCODE_ESCAPE;
  4562. _sapp.keycodes[0x7A] = SAPP_KEYCODE_F1;
  4563. _sapp.keycodes[0x78] = SAPP_KEYCODE_F2;
  4564. _sapp.keycodes[0x63] = SAPP_KEYCODE_F3;
  4565. _sapp.keycodes[0x76] = SAPP_KEYCODE_F4;
  4566. _sapp.keycodes[0x60] = SAPP_KEYCODE_F5;
  4567. _sapp.keycodes[0x61] = SAPP_KEYCODE_F6;
  4568. _sapp.keycodes[0x62] = SAPP_KEYCODE_F7;
  4569. _sapp.keycodes[0x64] = SAPP_KEYCODE_F8;
  4570. _sapp.keycodes[0x65] = SAPP_KEYCODE_F9;
  4571. _sapp.keycodes[0x6D] = SAPP_KEYCODE_F10;
  4572. _sapp.keycodes[0x67] = SAPP_KEYCODE_F11;
  4573. _sapp.keycodes[0x6F] = SAPP_KEYCODE_F12;
  4574. _sapp.keycodes[0x69] = SAPP_KEYCODE_F13;
  4575. _sapp.keycodes[0x6B] = SAPP_KEYCODE_F14;
  4576. _sapp.keycodes[0x71] = SAPP_KEYCODE_F15;
  4577. _sapp.keycodes[0x6A] = SAPP_KEYCODE_F16;
  4578. _sapp.keycodes[0x40] = SAPP_KEYCODE_F17;
  4579. _sapp.keycodes[0x4F] = SAPP_KEYCODE_F18;
  4580. _sapp.keycodes[0x50] = SAPP_KEYCODE_F19;
  4581. _sapp.keycodes[0x5A] = SAPP_KEYCODE_F20;
  4582. _sapp.keycodes[0x73] = SAPP_KEYCODE_HOME;
  4583. _sapp.keycodes[0x72] = SAPP_KEYCODE_INSERT;
  4584. _sapp.keycodes[0x7B] = SAPP_KEYCODE_LEFT;
  4585. _sapp.keycodes[0x3A] = SAPP_KEYCODE_LEFT_ALT;
  4586. _sapp.keycodes[0x3B] = SAPP_KEYCODE_LEFT_CONTROL;
  4587. _sapp.keycodes[0x38] = SAPP_KEYCODE_LEFT_SHIFT;
  4588. _sapp.keycodes[0x37] = SAPP_KEYCODE_LEFT_SUPER;
  4589. _sapp.keycodes[0x6E] = SAPP_KEYCODE_MENU;
  4590. _sapp.keycodes[0x47] = SAPP_KEYCODE_NUM_LOCK;
  4591. _sapp.keycodes[0x79] = SAPP_KEYCODE_PAGE_DOWN;
  4592. _sapp.keycodes[0x74] = SAPP_KEYCODE_PAGE_UP;
  4593. _sapp.keycodes[0x7C] = SAPP_KEYCODE_RIGHT;
  4594. _sapp.keycodes[0x3D] = SAPP_KEYCODE_RIGHT_ALT;
  4595. _sapp.keycodes[0x3E] = SAPP_KEYCODE_RIGHT_CONTROL;
  4596. _sapp.keycodes[0x3C] = SAPP_KEYCODE_RIGHT_SHIFT;
  4597. _sapp.keycodes[0x36] = SAPP_KEYCODE_RIGHT_SUPER;
  4598. _sapp.keycodes[0x31] = SAPP_KEYCODE_SPACE;
  4599. _sapp.keycodes[0x30] = SAPP_KEYCODE_TAB;
  4600. _sapp.keycodes[0x7E] = SAPP_KEYCODE_UP;
  4601. _sapp.keycodes[0x52] = SAPP_KEYCODE_KP_0;
  4602. _sapp.keycodes[0x53] = SAPP_KEYCODE_KP_1;
  4603. _sapp.keycodes[0x54] = SAPP_KEYCODE_KP_2;
  4604. _sapp.keycodes[0x55] = SAPP_KEYCODE_KP_3;
  4605. _sapp.keycodes[0x56] = SAPP_KEYCODE_KP_4;
  4606. _sapp.keycodes[0x57] = SAPP_KEYCODE_KP_5;
  4607. _sapp.keycodes[0x58] = SAPP_KEYCODE_KP_6;
  4608. _sapp.keycodes[0x59] = SAPP_KEYCODE_KP_7;
  4609. _sapp.keycodes[0x5B] = SAPP_KEYCODE_KP_8;
  4610. _sapp.keycodes[0x5C] = SAPP_KEYCODE_KP_9;
  4611. _sapp.keycodes[0x45] = SAPP_KEYCODE_KP_ADD;
  4612. _sapp.keycodes[0x41] = SAPP_KEYCODE_KP_DECIMAL;
  4613. _sapp.keycodes[0x4B] = SAPP_KEYCODE_KP_DIVIDE;
  4614. _sapp.keycodes[0x4C] = SAPP_KEYCODE_KP_ENTER;
  4615. _sapp.keycodes[0x51] = SAPP_KEYCODE_KP_EQUAL;
  4616. _sapp.keycodes[0x43] = SAPP_KEYCODE_KP_MULTIPLY;
  4617. _sapp.keycodes[0x4E] = SAPP_KEYCODE_KP_SUBTRACT;
  4618. }
  4619. _SOKOL_PRIVATE void _sapp_macos_discard_state(void) {
  4620. // NOTE: it's safe to call [release] on a nil object
  4621. if (_sapp.macos.keyup_monitor != nil) {
  4622. [NSEvent removeMonitor:_sapp.macos.keyup_monitor];
  4623. // NOTE: removeMonitor also releases the object
  4624. _sapp.macos.keyup_monitor = nil;
  4625. }
  4626. _SAPP_OBJC_RELEASE(_sapp.macos.tracking_area);
  4627. _SAPP_OBJC_RELEASE(_sapp.macos.app_dlg);
  4628. _SAPP_OBJC_RELEASE(_sapp.macos.win_dlg);
  4629. _SAPP_OBJC_RELEASE(_sapp.macos.view);
  4630. #if defined(SOKOL_METAL)
  4631. _sapp_macos_mtl_discard_state();
  4632. #elif defined(SOKOL_GLCORE)
  4633. _sapp_macos_gl_discard_state();
  4634. #elif defined(SOKOL_WGPU)
  4635. _sapp_macos_wgpu_discard_state();
  4636. #endif
  4637. _SAPP_OBJC_RELEASE(_sapp.macos.window);
  4638. }
  4639. // undocumented methods for creating cursors (see GLFW 3.4 and imgui_impl_osx.mm)
  4640. @interface NSCursor()
  4641. + (id)_windowResizeNorthWestSouthEastCursor;
  4642. + (id)_windowResizeNorthEastSouthWestCursor;
  4643. + (id)_windowResizeNorthSouthCursor;
  4644. + (id)_windowResizeEastWestCursor;
  4645. @end
  4646. _SOKOL_PRIVATE void _sapp_macos_init_cursors(void) {
  4647. for (size_t i = 0; i < _SAPP_MOUSECURSOR_NUM; i++) {
  4648. _sapp.macos.standard_cursors[i] = nil;
  4649. _sapp.macos.custom_cursors[i] = nil;
  4650. }
  4651. _sapp.macos.standard_cursors[SAPP_MOUSECURSOR_ARROW] = [NSCursor arrowCursor];
  4652. _sapp.macos.standard_cursors[SAPP_MOUSECURSOR_IBEAM] = [NSCursor IBeamCursor];
  4653. _sapp.macos.standard_cursors[SAPP_MOUSECURSOR_CROSSHAIR] = [NSCursor crosshairCursor];
  4654. _sapp.macos.standard_cursors[SAPP_MOUSECURSOR_POINTING_HAND] = [NSCursor pointingHandCursor];
  4655. _sapp.macos.standard_cursors[SAPP_MOUSECURSOR_RESIZE_EW] = [NSCursor respondsToSelector:@selector(_windowResizeEastWestCursor)] ? [NSCursor _windowResizeEastWestCursor] : [NSCursor resizeLeftRightCursor];
  4656. _sapp.macos.standard_cursors[SAPP_MOUSECURSOR_RESIZE_NS] = [NSCursor respondsToSelector:@selector(_windowResizeNorthSouthCursor)] ? [NSCursor _windowResizeNorthSouthCursor] : [NSCursor resizeUpDownCursor];
  4657. _sapp.macos.standard_cursors[SAPP_MOUSECURSOR_RESIZE_NWSE] = [NSCursor respondsToSelector:@selector(_windowResizeNorthWestSouthEastCursor)] ? [NSCursor _windowResizeNorthWestSouthEastCursor] : [NSCursor closedHandCursor];
  4658. _sapp.macos.standard_cursors[SAPP_MOUSECURSOR_RESIZE_NESW] = [NSCursor respondsToSelector:@selector(_windowResizeNorthEastSouthWestCursor)] ? [NSCursor _windowResizeNorthEastSouthWestCursor] : [NSCursor closedHandCursor];
  4659. _sapp.macos.standard_cursors[SAPP_MOUSECURSOR_RESIZE_ALL] = [NSCursor closedHandCursor];
  4660. _sapp.macos.standard_cursors[SAPP_MOUSECURSOR_NOT_ALLOWED] = [NSCursor operationNotAllowedCursor];
  4661. }
  4662. _SOKOL_PRIVATE void _sapp_macos_run(const sapp_desc* desc) {
  4663. _sapp_init_state(desc);
  4664. _sapp_macos_init_keytable();
  4665. [NSApplication sharedApplication];
  4666. // set the application dock icon as early as possible, otherwise
  4667. // the dummy icon will be visible for a short time
  4668. sapp_set_icon(&_sapp.desc.icon);
  4669. _sapp.macos.app_dlg = [[_sapp_macos_app_delegate alloc] init];
  4670. NSApp.delegate = _sapp.macos.app_dlg;
  4671. // workaround for "no key-up sent while Cmd is pressed" taken from GLFW:
  4672. NSEvent* (^keyup_monitor)(NSEvent*) = ^NSEvent* (NSEvent* event) {
  4673. if ([event modifierFlags] & NSEventModifierFlagCommand) {
  4674. [[NSApp keyWindow] sendEvent:event];
  4675. }
  4676. return event;
  4677. };
  4678. _sapp.macos.keyup_monitor = [NSEvent addLocalMonitorForEventsMatchingMask:NSEventMaskKeyUp handler:keyup_monitor];
  4679. [NSApp run];
  4680. // NOTE: [NSApp run] never returns, instead cleanup code
  4681. // must be put into applicationWillTerminate
  4682. }
  4683. /* MacOS entry function */
  4684. #if !defined(SOKOL_NO_ENTRY)
  4685. int main(int argc, char* argv[]) {
  4686. sapp_desc desc = sokol_main(argc, argv);
  4687. _sapp_macos_run(&desc);
  4688. return 0;
  4689. }
  4690. #endif /* SOKOL_NO_ENTRY */
  4691. _SOKOL_PRIVATE uint32_t _sapp_macos_mods(NSEvent* ev) {
  4692. const NSEventModifierFlags f = (ev == nil) ? NSEvent.modifierFlags : ev.modifierFlags;
  4693. const NSUInteger b = NSEvent.pressedMouseButtons;
  4694. uint32_t m = 0;
  4695. if (f & NSEventModifierFlagShift) {
  4696. m |= SAPP_MODIFIER_SHIFT;
  4697. }
  4698. if (f & NSEventModifierFlagControl) {
  4699. m |= SAPP_MODIFIER_CTRL;
  4700. }
  4701. if (f & NSEventModifierFlagOption) {
  4702. m |= SAPP_MODIFIER_ALT;
  4703. }
  4704. if (f & NSEventModifierFlagCommand) {
  4705. m |= SAPP_MODIFIER_SUPER;
  4706. }
  4707. if (0 != (b & (1<<0))) {
  4708. m |= SAPP_MODIFIER_LMB;
  4709. }
  4710. if (0 != (b & (1<<1))) {
  4711. m |= SAPP_MODIFIER_RMB;
  4712. }
  4713. if (0 != (b & (1<<2))) {
  4714. m |= SAPP_MODIFIER_MMB;
  4715. }
  4716. return m;
  4717. }
  4718. _SOKOL_PRIVATE void _sapp_macos_mouse_event(sapp_event_type type, sapp_mousebutton btn, uint32_t mod) {
  4719. if (_sapp_events_enabled()) {
  4720. _sapp_init_event(type);
  4721. _sapp.event.mouse_button = btn;
  4722. _sapp.event.modifiers = mod;
  4723. _sapp_call_event(&_sapp.event);
  4724. }
  4725. }
  4726. _SOKOL_PRIVATE void _sapp_macos_key_event(sapp_event_type type, sapp_keycode key, bool repeat, uint32_t mod) {
  4727. if (_sapp_events_enabled()) {
  4728. _sapp_init_event(type);
  4729. _sapp.event.key_code = key;
  4730. _sapp.event.key_repeat = repeat;
  4731. _sapp.event.modifiers = mod;
  4732. _sapp_call_event(&_sapp.event);
  4733. }
  4734. }
  4735. _SOKOL_PRIVATE void _sapp_macos_app_event(sapp_event_type type) {
  4736. if (_sapp_events_enabled()) {
  4737. _sapp_init_event(type);
  4738. _sapp_call_event(&_sapp.event);
  4739. }
  4740. }
  4741. // called in applicationDidFinishedLaunching when no window size was provided
  4742. _SOKOL_PRIVATE void _sapp_macos_init_default_dimensions(void) {
  4743. if (_sapp.desc.high_dpi) {
  4744. _sapp.dpi_scale = NSScreen.mainScreen.backingScaleFactor;
  4745. } else {
  4746. _sapp.dpi_scale = 1.0f;
  4747. }
  4748. NSRect screen_rect = NSScreen.mainScreen.frame;
  4749. // use 4/5 of screen size as default size
  4750. const float default_widthf = (screen_rect.size.width * 4.0f) / 5.0f;
  4751. const float default_heightf = (screen_rect.size.height * 4.0f) / 5.0f;
  4752. if (_sapp.window_width == 0) {
  4753. _sapp.window_width = _sapp_roundf_gzero(default_widthf);
  4754. }
  4755. if (_sapp.window_height == 0) {
  4756. _sapp.window_height = _sapp_roundf_gzero(default_heightf);
  4757. }
  4758. _sapp.framebuffer_width = _sapp_roundf_gzero(default_widthf * _sapp.dpi_scale);
  4759. _sapp.framebuffer_height = _sapp_roundf_gzero(default_heightf * _sapp.dpi_scale);
  4760. }
  4761. /* NOTE: unlike the iOS version of this function, the macOS version
  4762. can dynamically update the DPI scaling factor when a window is moved
  4763. between HighDPI / LowDPI screens.
  4764. */
  4765. _SOKOL_PRIVATE void _sapp_macos_update_dimensions(void) {
  4766. if (_sapp.desc.high_dpi) {
  4767. _sapp.dpi_scale = [_sapp.macos.window screen].backingScaleFactor;
  4768. } else {
  4769. _sapp.dpi_scale = 1.0f;
  4770. }
  4771. // NOTE: needed because we set layerContentsPlacement to a non-scaling value in windowWillStartLiveResize.
  4772. _sapp.macos.view.layer.contentsScale = _sapp.dpi_scale;
  4773. const NSRect bounds = [_sapp.macos.view bounds];
  4774. _sapp.window_width = _sapp_roundf_gzero(bounds.size.width);
  4775. _sapp.window_height = _sapp_roundf_gzero(bounds.size.height);
  4776. #if defined(SOKOL_METAL)
  4777. bool dim_changed = _sapp_macos_mtl_update_framebuffer_dimensions(bounds);
  4778. #elif defined(SOKOL_GLCORE)
  4779. bool dim_changed = _sapp_macos_gl_update_framebuffer_dimensions(bounds);
  4780. #elif defined(SOKOL_WGPU)
  4781. bool dim_changed = _sapp_macos_wgpu_update_framebuffer_dimensions(bounds);
  4782. #endif
  4783. if (dim_changed && !_sapp.first_frame) {
  4784. _sapp_macos_app_event(SAPP_EVENTTYPE_RESIZED);
  4785. }
  4786. }
  4787. _SOKOL_PRIVATE void _sapp_macos_toggle_fullscreen(void) {
  4788. /* NOTE: the _sapp.fullscreen flag is also notified by the
  4789. windowDidEnterFullscreen / windowDidExitFullscreen
  4790. event handlers
  4791. */
  4792. _sapp.fullscreen = !_sapp.fullscreen;
  4793. [_sapp.macos.window toggleFullScreen:nil];
  4794. }
  4795. _SOKOL_PRIVATE void _sapp_macos_set_clipboard_string(const char* str) {
  4796. @autoreleasepool {
  4797. NSPasteboard* pasteboard = [NSPasteboard generalPasteboard];
  4798. [pasteboard declareTypes:@[NSPasteboardTypeString] owner:nil];
  4799. [pasteboard setString:@(str) forType:NSPasteboardTypeString];
  4800. }
  4801. }
  4802. _SOKOL_PRIVATE const char* _sapp_macos_get_clipboard_string(void) {
  4803. SOKOL_ASSERT(_sapp.clipboard.buffer);
  4804. @autoreleasepool {
  4805. _sapp.clipboard.buffer[0] = 0;
  4806. NSPasteboard* pasteboard = [NSPasteboard generalPasteboard];
  4807. if (![[pasteboard types] containsObject:NSPasteboardTypeString]) {
  4808. return _sapp.clipboard.buffer;
  4809. }
  4810. NSString* str = [pasteboard stringForType:NSPasteboardTypeString];
  4811. if (!str) {
  4812. return _sapp.clipboard.buffer;
  4813. }
  4814. _sapp_strcpy([str UTF8String], _sapp.clipboard.buffer, (size_t)_sapp.clipboard.buf_size);
  4815. }
  4816. return _sapp.clipboard.buffer;
  4817. }
  4818. _SOKOL_PRIVATE void _sapp_macos_update_window_title(void) {
  4819. [_sapp.macos.window setTitle: [NSString stringWithUTF8String:_sapp.window_title]];
  4820. }
  4821. _SOKOL_PRIVATE void _sapp_macos_mouse_update_from_nspoint(NSPoint mouse_pos, bool clear_dxdy) {
  4822. if (!_sapp.mouse.locked) {
  4823. float new_x = mouse_pos.x * _sapp.dpi_scale;
  4824. float new_y = _sapp.framebuffer_height - (mouse_pos.y * _sapp.dpi_scale) - 1;
  4825. if (clear_dxdy) {
  4826. _sapp.mouse.dx = 0.0f;
  4827. _sapp.mouse.dy = 0.0f;
  4828. } else if (_sapp.mouse.pos_valid) {
  4829. // don't update dx/dy in the very first update
  4830. _sapp.mouse.dx = new_x - _sapp.mouse.x;
  4831. _sapp.mouse.dy = new_y - _sapp.mouse.y;
  4832. }
  4833. _sapp.mouse.x = new_x;
  4834. _sapp.mouse.y = new_y;
  4835. _sapp.mouse.pos_valid = true;
  4836. }
  4837. }
  4838. _SOKOL_PRIVATE void _sapp_macos_mouse_update_from_nsevent(NSEvent* event, bool clear_dxdy) {
  4839. _sapp_macos_mouse_update_from_nspoint(event.locationInWindow, clear_dxdy);
  4840. }
  4841. _SOKOL_PRIVATE void _sapp_macos_show_mouse(bool visible) {
  4842. /* NOTE: this function is only called when the mouse visibility actually changes */
  4843. if (visible) {
  4844. CGDisplayShowCursor(kCGDirectMainDisplay);
  4845. } else {
  4846. CGDisplayHideCursor(kCGDirectMainDisplay);
  4847. }
  4848. }
  4849. _SOKOL_PRIVATE void _sapp_macos_lock_mouse(bool lock) {
  4850. if (lock == _sapp.mouse.locked) {
  4851. return;
  4852. }
  4853. _sapp.mouse.dx = 0.0f;
  4854. _sapp.mouse.dy = 0.0f;
  4855. _sapp.mouse.locked = lock;
  4856. /*
  4857. NOTE that this code doesn't warp the mouse cursor to the window
  4858. center as everybody else does it. This lead to a spike in the
  4859. *second* mouse-moved event after the warp happened. The
  4860. mouse centering doesn't seem to be required (mouse-moved events
  4861. are reported correctly even when the cursor is at an edge of the screen).
  4862. NOTE also that the hide/show of the mouse cursor should properly
  4863. stack with calls to sapp_show_mouse()
  4864. */
  4865. if (_sapp.mouse.locked) {
  4866. CGAssociateMouseAndMouseCursorPosition(NO);
  4867. [NSCursor hide];
  4868. } else {
  4869. [NSCursor unhide];
  4870. CGAssociateMouseAndMouseCursorPosition(YES);
  4871. }
  4872. }
  4873. _SOKOL_PRIVATE void _sapp_macos_update_cursor(sapp_mouse_cursor cursor, bool shown) {
  4874. // show/hide cursor only if visibility status has changed (required because show/hide stacks)
  4875. if (shown != _sapp.mouse.shown) {
  4876. if (shown) {
  4877. [NSCursor unhide];
  4878. } else {
  4879. [NSCursor hide];
  4880. }
  4881. }
  4882. // update cursor
  4883. SOKOL_ASSERT((cursor >= 0) && (cursor < _SAPP_MOUSECURSOR_NUM));
  4884. NSCursor* ns_cursor = 0;
  4885. if (_sapp.custom_cursor_bound[cursor]) {
  4886. SOKOL_ASSERT(_sapp.macos.custom_cursors[cursor]);
  4887. ns_cursor = _sapp.macos.custom_cursors[cursor];
  4888. } else if (_sapp.macos.standard_cursors[cursor]) {
  4889. ns_cursor = _sapp.macos.standard_cursors[cursor];
  4890. } else {
  4891. ns_cursor = [NSCursor arrowCursor];
  4892. }
  4893. [ns_cursor set];
  4894. }
  4895. _SOKOL_PRIVATE bool _sapp_macos_make_custom_mouse_cursor(sapp_mouse_cursor cursor, const sapp_image_desc* desc) {
  4896. SOKOL_ASSERT((cursor >= 0) && (cursor < _SAPP_MOUSECURSOR_NUM));
  4897. SOKOL_ASSERT(_sapp.macos.custom_cursors[cursor] == nil);
  4898. // NOTE: see glfw for reference https://github.com/glfw/glfw/blob/ac10768495837eb98da27d01fe706073d6d251c2/src/cocoa_window.m#L1712
  4899. NSBitmapImageRep* rep = [[NSBitmapImageRep alloc]
  4900. initWithBitmapDataPlanes:NULL
  4901. pixelsWide:desc->width
  4902. pixelsHigh:desc->height
  4903. bitsPerSample:8
  4904. samplesPerPixel:4
  4905. hasAlpha:YES
  4906. isPlanar:NO
  4907. colorSpaceName:NSCalibratedRGBColorSpace
  4908. bitmapFormat:NSBitmapFormatAlphaNonpremultiplied
  4909. bytesPerRow:desc->width * 4
  4910. bitsPerPixel:32];
  4911. if (rep != nil) {
  4912. memcpy([rep bitmapData], desc->pixels.ptr, (size_t) (desc->width * desc->height * 4));
  4913. NSImage* native = [[NSImage alloc] initWithSize:NSMakeSize(desc->width, desc->height)];
  4914. SOKOL_ASSERT(native);
  4915. [native addRepresentation:rep];
  4916. _sapp.macos.custom_cursors[cursor] = [[NSCursor alloc]
  4917. initWithImage:native
  4918. hotSpot:NSMakePoint(desc->cursor_hotspot_x, desc->cursor_hotspot_y)];
  4919. SOKOL_ASSERT(_sapp.macos.custom_cursors[cursor] != nil);
  4920. _SAPP_OBJC_RELEASE(native);
  4921. _SAPP_OBJC_RELEASE(rep);
  4922. return true;
  4923. }
  4924. return false;
  4925. }
  4926. _SOKOL_PRIVATE void _sapp_macos_destroy_custom_mouse_cursor(sapp_mouse_cursor cursor) {
  4927. SOKOL_ASSERT((cursor >= 0) && (cursor < _SAPP_MOUSECURSOR_NUM));
  4928. SOKOL_ASSERT(_sapp.macos.custom_cursors[cursor] != nil);
  4929. _SAPP_OBJC_RELEASE(_sapp.macos.custom_cursors[cursor]);
  4930. }
  4931. _SOKOL_PRIVATE void _sapp_macos_set_icon(const sapp_icon_desc* icon_desc, int num_images) {
  4932. NSDockTile* dock_tile = NSApp.dockTile;
  4933. const int wanted_width = (int) dock_tile.size.width;
  4934. const int wanted_height = (int) dock_tile.size.height;
  4935. const int img_index = _sapp_image_bestmatch(icon_desc->images, num_images, wanted_width, wanted_height);
  4936. const sapp_image_desc* img_desc = &icon_desc->images[img_index];
  4937. CGColorSpaceRef cg_color_space = CGColorSpaceCreateDeviceRGB();
  4938. CFDataRef cf_data = CFDataCreate(kCFAllocatorDefault, (const UInt8*)img_desc->pixels.ptr, (CFIndex)img_desc->pixels.size);
  4939. CGDataProviderRef cg_data_provider = CGDataProviderCreateWithCFData(cf_data);
  4940. CGImageRef cg_img = CGImageCreate(
  4941. (size_t)img_desc->width, // width
  4942. (size_t)img_desc->height, // height
  4943. 8, // bitsPerComponent
  4944. 32, // bitsPerPixel
  4945. (size_t)img_desc->width * 4,// bytesPerRow
  4946. cg_color_space, // space
  4947. kCGImageAlphaLast | kCGImageByteOrderDefault, // bitmapInfo
  4948. cg_data_provider, // provider
  4949. NULL, // decode
  4950. false, // shouldInterpolate
  4951. kCGRenderingIntentDefault);
  4952. CFRelease(cf_data);
  4953. CGDataProviderRelease(cg_data_provider);
  4954. CGColorSpaceRelease(cg_color_space);
  4955. NSImage* ns_image = [[NSImage alloc] initWithCGImage:cg_img size:dock_tile.size];
  4956. dock_tile.contentView = [NSImageView imageViewWithImage:ns_image];
  4957. [dock_tile display];
  4958. _SAPP_OBJC_RELEASE(ns_image);
  4959. CGImageRelease(cg_img);
  4960. }
  4961. _SOKOL_PRIVATE void _sapp_macos_frame(void) {
  4962. // NOTE: DO NOT call _sapp_macos_update_dimensions() function from within the
  4963. // frame callback (at least when called from MTKView's drawRect function).
  4964. // This will trigger a chicken-egg situation that triggers a
  4965. // Metal validation layer error about different render target sizes.
  4966. _sapp_timing_measure(&_sapp.timing);
  4967. #if defined(_SAPP_ANY_GL)
  4968. glGetIntegerv(GL_FRAMEBUFFER_BINDING, (GLint*)&_sapp.gl.framebuffer);
  4969. #endif
  4970. @autoreleasepool {
  4971. #if defined(SOKOL_WGPU)
  4972. _sapp_wgpu_frame();
  4973. #else
  4974. _sapp_frame();
  4975. #endif
  4976. }
  4977. #if defined(_SAPP_ANY_GL)
  4978. [[_sapp.macos.view openGLContext] flushBuffer];
  4979. #endif
  4980. if (_sapp.quit_requested || _sapp.quit_ordered) {
  4981. [_sapp.macos.window performClose:nil];
  4982. }
  4983. }
  4984. @implementation _sapp_macos_app_delegate
  4985. - (void)applicationDidFinishLaunching:(NSNotification*)aNotification {
  4986. _SOKOL_UNUSED(aNotification);
  4987. _sapp_macos_init_cursors();
  4988. if ((_sapp.window_width == 0) || (_sapp.window_height == 0)) {
  4989. _sapp_macos_init_default_dimensions();
  4990. }
  4991. const NSUInteger style =
  4992. NSWindowStyleMaskTitled |
  4993. NSWindowStyleMaskClosable |
  4994. NSWindowStyleMaskMiniaturizable |
  4995. NSWindowStyleMaskResizable;
  4996. NSRect window_rect = NSMakeRect(0, 0, _sapp.window_width, _sapp.window_height);
  4997. _sapp.macos.window = [[_sapp_macos_window alloc]
  4998. initWithContentRect:window_rect
  4999. styleMask:style
  5000. backing:NSBackingStoreBuffered
  5001. defer:NO];
  5002. _sapp.macos.window.releasedWhenClosed = NO; // this is necessary for proper cleanup in applicationWillTerminate
  5003. _sapp.macos.window.title = [NSString stringWithUTF8String:_sapp.window_title];
  5004. _sapp.macos.window.acceptsMouseMovedEvents = YES;
  5005. _sapp.macos.window.restorable = YES;
  5006. _sapp.macos.win_dlg = [[_sapp_macos_window_delegate alloc] init];
  5007. _sapp.macos.window.delegate = _sapp.macos.win_dlg;
  5008. #if defined(SOKOL_METAL)
  5009. _sapp_macos_mtl_init();
  5010. #elif defined(SOKOL_GLCORE)
  5011. _sapp_macos_gl_init(window_rect);
  5012. #elif defined(SOKOL_WGPU)
  5013. _sapp_macos_wgpu_init();
  5014. #endif
  5015. _sapp.macos.window.contentView = _sapp.macos.view;
  5016. [_sapp.macos.window makeFirstResponder:_sapp.macos.view];
  5017. [_sapp.macos.window center];
  5018. _sapp.valid = true;
  5019. NSApp.activationPolicy = NSApplicationActivationPolicyRegular;
  5020. if (_sapp.fullscreen) {
  5021. /* ^^^ on GL, this already toggles a rendered frame, so set the valid flag before */
  5022. [_sapp.macos.window toggleFullScreen:self];
  5023. }
  5024. [NSApp activateIgnoringOtherApps:YES];
  5025. [_sapp.macos.window makeKeyAndOrderFront:nil];
  5026. _sapp_macos_update_dimensions();
  5027. [NSEvent setMouseCoalescingEnabled:NO];
  5028. // workaround for window not being focused during a long init callback
  5029. // for details see: https://github.com/floooh/sokol/pull/982
  5030. // also see: https://gitlab.gnome.org/GNOME/gtk/-/issues/2342
  5031. NSEvent *focusevent = [NSEvent otherEventWithType:NSEventTypeAppKitDefined
  5032. location:NSZeroPoint
  5033. modifierFlags:0x40
  5034. timestamp:0
  5035. windowNumber:0
  5036. context:nil
  5037. subtype:NSEventSubtypeApplicationActivated
  5038. data1:0
  5039. data2:0];
  5040. [NSApp postEvent:focusevent atStart:YES];
  5041. }
  5042. - (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication*)sender {
  5043. _SOKOL_UNUSED(sender);
  5044. return YES;
  5045. }
  5046. - (void)applicationWillTerminate:(NSNotification*)notification {
  5047. _SOKOL_UNUSED(notification);
  5048. _sapp_call_cleanup();
  5049. _sapp_macos_discard_state();
  5050. _sapp_discard_state();
  5051. }
  5052. @end
  5053. @implementation _sapp_macos_window_delegate
  5054. - (BOOL)windowShouldClose:(id)sender {
  5055. _SOKOL_UNUSED(sender);
  5056. // only give user-code a chance to intervene when sapp_quit() wasn't already called
  5057. if (!_sapp.quit_ordered) {
  5058. // if window should be closed and event handling is enabled, give user code
  5059. // a chance to intervene via sapp_cancel_quit()
  5060. _sapp.quit_requested = true;
  5061. _sapp_macos_app_event(SAPP_EVENTTYPE_QUIT_REQUESTED);
  5062. /* user code hasn't intervened, quit the app */
  5063. if (_sapp.quit_requested) {
  5064. _sapp.quit_ordered = true;
  5065. }
  5066. }
  5067. if (_sapp.quit_ordered) {
  5068. return YES;
  5069. } else {
  5070. return NO;
  5071. }
  5072. }
  5073. - (void)windowWillStartLiveResize:(NSNotification *)notification {
  5074. #if defined(SOKOL_METAL) || defined(SOKOL_WGPU)
  5075. // Work around the MTKView/CAMetalLayer resizing glitch by "anchoring" the layer to the window corner opposite
  5076. // to the currently manipulated corner (or edge). This prevents the content stretching back and
  5077. // forth during resizing. This is a workaround for this issue: https://github.com/floooh/sokol/issues/700
  5078. // Can be removed if/when migrating to CAMetalLayer: https://github.com/floooh/sokol/issues/727
  5079. bool resizing_from_left = _sapp.mouse.x < _sapp.window_width/2;
  5080. bool resizing_from_top = _sapp.mouse.y < _sapp.window_height/2;
  5081. NSViewLayerContentsPlacement placement;
  5082. if (resizing_from_left) {
  5083. placement = resizing_from_top ? NSViewLayerContentsPlacementBottomRight : NSViewLayerContentsPlacementTopRight;
  5084. } else {
  5085. placement = resizing_from_top ? NSViewLayerContentsPlacementBottomLeft : NSViewLayerContentsPlacementTopLeft;
  5086. }
  5087. _sapp.macos.view.layerContentsPlacement = placement;
  5088. #endif
  5089. }
  5090. - (void)windowDidResize:(NSNotification*)notification {
  5091. _SOKOL_UNUSED(notification);
  5092. _sapp_macos_update_dimensions();
  5093. }
  5094. - (void)windowDidChangeScreen:(NSNotification*)notification {
  5095. _SOKOL_UNUSED(notification);
  5096. _sapp_timing_reset(&_sapp.timing);
  5097. _sapp_macos_update_dimensions();
  5098. }
  5099. - (void)windowDidMiniaturize:(NSNotification*)notification {
  5100. _SOKOL_UNUSED(notification);
  5101. _sapp_macos_app_event(SAPP_EVENTTYPE_ICONIFIED);
  5102. }
  5103. - (void)windowDidDeminiaturize:(NSNotification*)notification {
  5104. _SOKOL_UNUSED(notification);
  5105. _sapp_macos_app_event(SAPP_EVENTTYPE_RESTORED);
  5106. }
  5107. - (void)windowDidBecomeKey:(NSNotification*)notification {
  5108. _SOKOL_UNUSED(notification);
  5109. _sapp_macos_app_event(SAPP_EVENTTYPE_FOCUSED);
  5110. }
  5111. - (void)windowDidResignKey:(NSNotification*)notification {
  5112. _SOKOL_UNUSED(notification);
  5113. _sapp_macos_app_event(SAPP_EVENTTYPE_UNFOCUSED);
  5114. }
  5115. - (void)windowDidEnterFullScreen:(NSNotification*)notification {
  5116. _SOKOL_UNUSED(notification);
  5117. _sapp.fullscreen = true;
  5118. }
  5119. - (void)windowDidExitFullScreen:(NSNotification*)notification {
  5120. _SOKOL_UNUSED(notification);
  5121. _sapp.fullscreen = false;
  5122. }
  5123. @end
  5124. @implementation _sapp_macos_window
  5125. - (instancetype)initWithContentRect:(NSRect)contentRect
  5126. styleMask:(NSWindowStyleMask)style
  5127. backing:(NSBackingStoreType)backingStoreType
  5128. defer:(BOOL)flag {
  5129. if (self = [super initWithContentRect:contentRect styleMask:style backing:backingStoreType defer:flag]) {
  5130. #if __MAC_OS_X_VERSION_MAX_ALLOWED >= 101300
  5131. [self registerForDraggedTypes:[NSArray arrayWithObject:NSPasteboardTypeFileURL]];
  5132. #endif
  5133. }
  5134. return self;
  5135. }
  5136. - (NSDragOperation)draggingEntered:(id<NSDraggingInfo>)sender {
  5137. return NSDragOperationCopy;
  5138. }
  5139. - (NSDragOperation)draggingUpdated:(id<NSDraggingInfo>)sender {
  5140. return NSDragOperationCopy;
  5141. }
  5142. - (BOOL)performDragOperation:(id<NSDraggingInfo>)sender {
  5143. #if __MAC_OS_X_VERSION_MAX_ALLOWED >= 101300
  5144. NSPasteboard *pboard = [sender draggingPasteboard];
  5145. if ([pboard.types containsObject:NSPasteboardTypeFileURL]) {
  5146. _sapp_clear_drop_buffer();
  5147. _sapp.drop.num_files = ((int)pboard.pasteboardItems.count > _sapp.drop.max_files) ? _sapp.drop.max_files : (int)pboard.pasteboardItems.count;
  5148. bool drop_failed = false;
  5149. for (int i = 0; i < _sapp.drop.num_files; i++) {
  5150. NSURL *fileUrl = [NSURL fileURLWithPath:[pboard.pasteboardItems[(NSUInteger)i] stringForType:NSPasteboardTypeFileURL]];
  5151. if (!_sapp_strcpy(fileUrl.standardizedURL.path.UTF8String, _sapp_dropped_file_path_ptr(i), (size_t)_sapp.drop.max_path_length)) {
  5152. _SAPP_ERROR(DROPPED_FILE_PATH_TOO_LONG);
  5153. drop_failed = true;
  5154. break;
  5155. }
  5156. }
  5157. if (!drop_failed) {
  5158. if (_sapp_events_enabled()) {
  5159. _sapp_macos_mouse_update_from_nspoint(sender.draggingLocation, true);
  5160. _sapp_init_event(SAPP_EVENTTYPE_FILES_DROPPED);
  5161. _sapp.event.modifiers = _sapp_macos_mods(nil);
  5162. _sapp_call_event(&_sapp.event);
  5163. }
  5164. } else {
  5165. _sapp_clear_drop_buffer();
  5166. _sapp.drop.num_files = 0;
  5167. }
  5168. return YES;
  5169. }
  5170. #endif
  5171. return NO;
  5172. }
  5173. @end
  5174. @implementation _sapp_macos_view
  5175. #if defined(SOKOL_GLCORE)
  5176. - (void)timerFired:(id)sender {
  5177. _SOKOL_UNUSED(sender);
  5178. [self setNeedsDisplay:YES];
  5179. }
  5180. - (void)prepareOpenGL {
  5181. [super prepareOpenGL];
  5182. GLint swapInt = 1;
  5183. NSOpenGLContext* ctx = [_sapp.macos.view openGLContext];
  5184. [ctx setValues:&swapInt forParameter:NSOpenGLContextParameterSwapInterval];
  5185. [ctx makeCurrentContext];
  5186. }
  5187. #endif
  5188. #if defined(SOKOL_WGPU)
  5189. - (void)displayLinkFired:(id)sender {
  5190. _SOKOL_UNUSED(sender);
  5191. _sapp_macos_frame();
  5192. }
  5193. #else
  5194. - (void)drawRect:(NSRect)rect {
  5195. _SOKOL_UNUSED(rect);
  5196. _sapp_macos_frame();
  5197. }
  5198. #endif
  5199. - (BOOL)isOpaque {
  5200. return YES;
  5201. }
  5202. - (BOOL)canBecomeKeyView {
  5203. return YES;
  5204. }
  5205. - (BOOL)acceptsFirstResponder {
  5206. return YES;
  5207. }
  5208. - (void)updateTrackingAreas {
  5209. if (_sapp.macos.tracking_area != nil) {
  5210. [self removeTrackingArea:_sapp.macos.tracking_area];
  5211. _SAPP_OBJC_RELEASE(_sapp.macos.tracking_area);
  5212. }
  5213. const NSTrackingAreaOptions options = NSTrackingMouseEnteredAndExited |
  5214. NSTrackingActiveInKeyWindow |
  5215. NSTrackingEnabledDuringMouseDrag |
  5216. NSTrackingCursorUpdate |
  5217. NSTrackingInVisibleRect |
  5218. NSTrackingAssumeInside;
  5219. _sapp.macos.tracking_area = [[NSTrackingArea alloc] initWithRect:[self bounds] options:options owner:self userInfo:nil];
  5220. [self addTrackingArea:_sapp.macos.tracking_area];
  5221. [super updateTrackingAreas];
  5222. }
  5223. // helper function to make GL context active
  5224. static void _sapp_gl_make_current(void) {
  5225. #if defined(SOKOL_GLCORE)
  5226. [[_sapp.macos.view openGLContext] makeCurrentContext];
  5227. #endif
  5228. }
  5229. - (void)mouseEntered:(NSEvent*)event {
  5230. _sapp_gl_make_current();
  5231. _sapp_macos_mouse_update_from_nsevent(event, true);
  5232. /* don't send mouse enter/leave while dragging (so that it behaves the same as
  5233. on Windows while SetCapture is active
  5234. */
  5235. if (0 == _sapp.macos.mouse_buttons) {
  5236. _sapp_macos_mouse_event(SAPP_EVENTTYPE_MOUSE_ENTER, SAPP_MOUSEBUTTON_INVALID, _sapp_macos_mods(event));
  5237. }
  5238. }
  5239. - (void)mouseExited:(NSEvent*)event {
  5240. _sapp_gl_make_current();
  5241. _sapp_macos_mouse_update_from_nsevent(event, true);
  5242. if (0 == _sapp.macos.mouse_buttons) {
  5243. _sapp_macos_mouse_event(SAPP_EVENTTYPE_MOUSE_LEAVE, SAPP_MOUSEBUTTON_INVALID, _sapp_macos_mods(event));
  5244. }
  5245. }
  5246. - (void)mouseDown:(NSEvent*)event {
  5247. _sapp_gl_make_current();
  5248. _sapp_macos_mouse_update_from_nsevent(event, false);
  5249. _sapp_macos_mouse_event(SAPP_EVENTTYPE_MOUSE_DOWN, SAPP_MOUSEBUTTON_LEFT, _sapp_macos_mods(event));
  5250. _sapp.macos.mouse_buttons |= (1<<SAPP_MOUSEBUTTON_LEFT);
  5251. }
  5252. - (void)mouseUp:(NSEvent*)event {
  5253. _sapp_gl_make_current();
  5254. _sapp_macos_mouse_update_from_nsevent(event, false);
  5255. _sapp_macos_mouse_event(SAPP_EVENTTYPE_MOUSE_UP, SAPP_MOUSEBUTTON_LEFT, _sapp_macos_mods(event));
  5256. _sapp.macos.mouse_buttons &= ~(1<<SAPP_MOUSEBUTTON_LEFT);
  5257. }
  5258. - (void)rightMouseDown:(NSEvent*)event {
  5259. _sapp_gl_make_current();
  5260. _sapp_macos_mouse_update_from_nsevent(event, false);
  5261. _sapp_macos_mouse_event(SAPP_EVENTTYPE_MOUSE_DOWN, SAPP_MOUSEBUTTON_RIGHT, _sapp_macos_mods(event));
  5262. _sapp.macos.mouse_buttons |= (1<<SAPP_MOUSEBUTTON_RIGHT);
  5263. }
  5264. - (void)rightMouseUp:(NSEvent*)event {
  5265. _sapp_gl_make_current();
  5266. _sapp_macos_mouse_update_from_nsevent(event, false);
  5267. _sapp_macos_mouse_event(SAPP_EVENTTYPE_MOUSE_UP, SAPP_MOUSEBUTTON_RIGHT, _sapp_macos_mods(event));
  5268. _sapp.macos.mouse_buttons &= ~(1<<SAPP_MOUSEBUTTON_RIGHT);
  5269. }
  5270. - (void)otherMouseDown:(NSEvent*)event {
  5271. _sapp_gl_make_current();
  5272. _sapp_macos_mouse_update_from_nsevent(event, false);
  5273. if (2 == event.buttonNumber) {
  5274. _sapp_macos_mouse_event(SAPP_EVENTTYPE_MOUSE_DOWN, SAPP_MOUSEBUTTON_MIDDLE, _sapp_macos_mods(event));
  5275. _sapp.macos.mouse_buttons |= (1<<SAPP_MOUSEBUTTON_MIDDLE);
  5276. }
  5277. }
  5278. - (void)otherMouseUp:(NSEvent*)event {
  5279. _sapp_gl_make_current();
  5280. _sapp_macos_mouse_update_from_nsevent(event, false);
  5281. if (2 == event.buttonNumber) {
  5282. _sapp_macos_mouse_event(SAPP_EVENTTYPE_MOUSE_UP, SAPP_MOUSEBUTTON_MIDDLE, _sapp_macos_mods(event));
  5283. _sapp.macos.mouse_buttons &= (1<<SAPP_MOUSEBUTTON_MIDDLE);
  5284. }
  5285. }
  5286. - (void)otherMouseDragged:(NSEvent*)event {
  5287. _sapp_gl_make_current();
  5288. _sapp_macos_mouse_update_from_nsevent(event, false);
  5289. if (2 == event.buttonNumber) {
  5290. if (_sapp.mouse.locked) {
  5291. _sapp.mouse.dx = [event deltaX];
  5292. _sapp.mouse.dy = [event deltaY];
  5293. }
  5294. _sapp_macos_mouse_event(SAPP_EVENTTYPE_MOUSE_MOVE, SAPP_MOUSEBUTTON_INVALID, _sapp_macos_mods(event));
  5295. }
  5296. }
  5297. - (void)mouseMoved:(NSEvent*)event {
  5298. _sapp_gl_make_current();
  5299. _sapp_macos_mouse_update_from_nsevent(event, false);
  5300. if (_sapp.mouse.locked) {
  5301. _sapp.mouse.dx = [event deltaX];
  5302. _sapp.mouse.dy = [event deltaY];
  5303. }
  5304. _sapp_macos_mouse_event(SAPP_EVENTTYPE_MOUSE_MOVE, SAPP_MOUSEBUTTON_INVALID , _sapp_macos_mods(event));
  5305. }
  5306. - (void)mouseDragged:(NSEvent*)event {
  5307. _sapp_gl_make_current();
  5308. _sapp_macos_mouse_update_from_nsevent(event, false);
  5309. if (_sapp.mouse.locked) {
  5310. _sapp.mouse.dx = [event deltaX];
  5311. _sapp.mouse.dy = [event deltaY];
  5312. }
  5313. _sapp_macos_mouse_event(SAPP_EVENTTYPE_MOUSE_MOVE, SAPP_MOUSEBUTTON_INVALID , _sapp_macos_mods(event));
  5314. }
  5315. - (void)rightMouseDragged:(NSEvent*)event {
  5316. _sapp_gl_make_current();
  5317. _sapp_macos_mouse_update_from_nsevent(event, false);
  5318. if (_sapp.mouse.locked) {
  5319. _sapp.mouse.dx = [event deltaX];
  5320. _sapp.mouse.dy = [event deltaY];
  5321. }
  5322. _sapp_macos_mouse_event(SAPP_EVENTTYPE_MOUSE_MOVE, SAPP_MOUSEBUTTON_INVALID, _sapp_macos_mods(event));
  5323. }
  5324. - (void)scrollWheel:(NSEvent*)event {
  5325. _sapp_gl_make_current();
  5326. _sapp_macos_mouse_update_from_nsevent(event, true);
  5327. if (_sapp_events_enabled()) {
  5328. float dx = (float) event.scrollingDeltaX;
  5329. float dy = (float) event.scrollingDeltaY;
  5330. if (event.hasPreciseScrollingDeltas) {
  5331. dx *= 0.1;
  5332. dy *= 0.1;
  5333. }
  5334. if ((_sapp_absf(dx) > 0.0f) || (_sapp_absf(dy) > 0.0f)) {
  5335. _sapp_init_event(SAPP_EVENTTYPE_MOUSE_SCROLL);
  5336. _sapp.event.modifiers = _sapp_macos_mods(event);
  5337. _sapp.event.scroll_x = dx;
  5338. _sapp.event.scroll_y = dy;
  5339. _sapp_call_event(&_sapp.event);
  5340. }
  5341. }
  5342. }
  5343. - (void)keyDown:(NSEvent*)event {
  5344. if (_sapp_events_enabled()) {
  5345. _sapp_gl_make_current();
  5346. const uint32_t mods = _sapp_macos_mods(event);
  5347. const sapp_keycode key_code = _sapp_translate_key(event.keyCode);
  5348. _sapp_macos_key_event(SAPP_EVENTTYPE_KEY_DOWN, key_code, event.isARepeat, mods);
  5349. const NSString* chars = event.characters;
  5350. const NSUInteger len = chars.length;
  5351. if (len > 0) {
  5352. _sapp_init_event(SAPP_EVENTTYPE_CHAR);
  5353. _sapp.event.modifiers = mods;
  5354. for (NSUInteger i = 0; i < len; i++) {
  5355. const unichar codepoint = [chars characterAtIndex:i];
  5356. if ((codepoint & 0xFF00) == 0xF700) {
  5357. continue;
  5358. }
  5359. _sapp.event.char_code = codepoint;
  5360. _sapp.event.key_repeat = event.isARepeat;
  5361. _sapp_call_event(&_sapp.event);
  5362. }
  5363. }
  5364. /* if this is a Cmd+V (paste), also send a CLIPBOARD_PASTE event */
  5365. if (_sapp.clipboard.enabled && (mods == SAPP_MODIFIER_SUPER) && (key_code == SAPP_KEYCODE_V)) {
  5366. _sapp_init_event(SAPP_EVENTTYPE_CLIPBOARD_PASTED);
  5367. _sapp_call_event(&_sapp.event);
  5368. }
  5369. }
  5370. }
  5371. - (BOOL)performKeyEquivalent:(NSEvent*)event {
  5372. // fixes Ctrl-Tab keydown not triggering a keyDown event
  5373. //
  5374. // NOTE: it seems that Ctrl-F1 cannot be intercepted the same way, but since
  5375. // this enabled critical accessibility features that's probably a good thing.
  5376. switch (_sapp_translate_key(event.keyCode)) {
  5377. case SAPP_KEYCODE_TAB:
  5378. [_sapp.macos.view keyDown:event];
  5379. return YES;
  5380. default:
  5381. return NO;
  5382. }
  5383. }
  5384. - (void)keyUp:(NSEvent*)event {
  5385. _sapp_gl_make_current();
  5386. _sapp_macos_key_event(SAPP_EVENTTYPE_KEY_UP,
  5387. _sapp_translate_key(event.keyCode),
  5388. event.isARepeat,
  5389. _sapp_macos_mods(event));
  5390. }
  5391. - (void)flagsChanged:(NSEvent*)event {
  5392. const uint32_t old_f = _sapp.macos.flags_changed_store;
  5393. const uint32_t new_f = (uint32_t)event.modifierFlags;
  5394. _sapp.macos.flags_changed_store = new_f;
  5395. sapp_keycode key_code = SAPP_KEYCODE_INVALID;
  5396. bool down = false;
  5397. if ((new_f ^ old_f) & NSEventModifierFlagShift) {
  5398. key_code = SAPP_KEYCODE_LEFT_SHIFT;
  5399. down = 0 != (new_f & NSEventModifierFlagShift);
  5400. }
  5401. if ((new_f ^ old_f) & NSEventModifierFlagControl) {
  5402. key_code = SAPP_KEYCODE_LEFT_CONTROL;
  5403. down = 0 != (new_f & NSEventModifierFlagControl);
  5404. }
  5405. if ((new_f ^ old_f) & NSEventModifierFlagOption) {
  5406. key_code = SAPP_KEYCODE_LEFT_ALT;
  5407. down = 0 != (new_f & NSEventModifierFlagOption);
  5408. }
  5409. if ((new_f ^ old_f) & NSEventModifierFlagCommand) {
  5410. key_code = SAPP_KEYCODE_LEFT_SUPER;
  5411. down = 0 != (new_f & NSEventModifierFlagCommand);
  5412. }
  5413. if (key_code != SAPP_KEYCODE_INVALID) {
  5414. _sapp_macos_key_event(down ? SAPP_EVENTTYPE_KEY_DOWN : SAPP_EVENTTYPE_KEY_UP,
  5415. key_code,
  5416. false,
  5417. _sapp_macos_mods(event));
  5418. }
  5419. }
  5420. - (void)cursorUpdate:(NSEvent *)event {
  5421. _sapp_macos_update_cursor(_sapp.mouse.current_cursor, _sapp.mouse.shown);
  5422. }
  5423. @end
  5424. #endif // macOS
  5425. // ██ ██████ ███████
  5426. // ██ ██ ██ ██
  5427. // ██ ██ ██ ███████
  5428. // ██ ██ ██ ██
  5429. // ██ ██████ ███████
  5430. //
  5431. // >>ios
  5432. #if defined(_SAPP_IOS)
  5433. #if defined(SOKOL_METAL)
  5434. _SOKOL_PRIVATE void _sapp_ios_mtl_init(void) {
  5435. const NSInteger max_fps = UIScreen.mainScreen.maximumFramesPerSecond;
  5436. _sapp.ios.mtl_device = MTLCreateSystemDefaultDevice();
  5437. _sapp.ios.view = [[_sapp_ios_view alloc] init];
  5438. _sapp.ios.view.preferredFramesPerSecond = max_fps / _sapp.swap_interval;
  5439. _sapp.ios.view.device = _sapp.ios.mtl_device;
  5440. _sapp.ios.view.colorPixelFormat = MTLPixelFormatBGRA8Unorm;
  5441. _sapp.ios.view.depthStencilPixelFormat = MTLPixelFormatDepth32Float_Stencil8;
  5442. _sapp.ios.view.sampleCount = (NSUInteger)_sapp.sample_count;
  5443. /* NOTE: iOS MTKView seems to ignore thew view's contentScaleFactor
  5444. and automatically renders at Retina resolution. We'll disable
  5445. autoResize and instead do the resizing in _sapp_ios_update_dimensions()
  5446. */
  5447. _sapp.ios.view.autoResizeDrawable = false;
  5448. _sapp.ios.view.userInteractionEnabled = YES;
  5449. #if !defined(_SAPP_TVOS)
  5450. _sapp.ios.view.multipleTouchEnabled = YES;
  5451. #endif
  5452. _sapp.ios.view_ctrl = [[UIViewController alloc] init];
  5453. _sapp.ios.view_ctrl.modalPresentationStyle = UIModalPresentationFullScreen;
  5454. _sapp.ios.view_ctrl.view = _sapp.ios.view;
  5455. _sapp.ios.window.rootViewController = _sapp.ios.view_ctrl;
  5456. }
  5457. _SOKOL_PRIVATE void _sapp_ios_mtl_discard_state(void) {
  5458. _SAPP_OBJC_RELEASE(_sapp.ios.view_ctrl);
  5459. _SAPP_OBJC_RELEASE(_sapp.ios.mtl_device);
  5460. }
  5461. _SOKOL_PRIVATE bool _sapp_ios_mtl_update_framebuffer_dimensions(CGRect screen_rect) {
  5462. _sapp.framebuffer_width = _sapp_roundf_gzero(screen_rect.size.width * _sapp.dpi_scale);
  5463. _sapp.framebuffer_height = _sapp_roundf_gzero(screen_rect.size.height * _sapp.dpi_scale);
  5464. const CGSize fb_size = _sapp.ios.view.drawableSize;
  5465. int cur_fb_width = _sapp_roundf_gzero(fb_size.width);
  5466. int cur_fb_height = _sapp_roundf_gzero(fb_size.height);
  5467. bool dim_changed = (_sapp.framebuffer_width != cur_fb_width) || (_sapp.framebuffer_height != cur_fb_height);
  5468. if (dim_changed) {
  5469. const CGSize drawable_size = { (CGFloat) _sapp.framebuffer_width, (CGFloat) _sapp.framebuffer_height };
  5470. _sapp.ios.view.drawableSize = drawable_size;
  5471. }
  5472. return dim_changed;
  5473. }
  5474. #endif
  5475. #if defined(SOKOL_GLES3)
  5476. _SOKOL_PRIVATE void _sapp_ios_gles3_init(CGRect screen_rect) {
  5477. const NSInteger max_fps = UIScreen.mainScreen.maximumFramesPerSecond;
  5478. _sapp.ios.eagl_ctx = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES3];
  5479. _sapp.ios.view = [[_sapp_ios_view alloc] initWithFrame:screen_rect];
  5480. _sapp.ios.view.drawableColorFormat = GLKViewDrawableColorFormatRGBA8888;
  5481. _sapp.ios.view.drawableDepthFormat = GLKViewDrawableDepthFormat24;
  5482. _sapp.ios.view.drawableStencilFormat = GLKViewDrawableStencilFormatNone;
  5483. GLKViewDrawableMultisample msaa = _sapp.sample_count > 1 ? GLKViewDrawableMultisample4X : GLKViewDrawableMultisampleNone;
  5484. _sapp.ios.view.drawableMultisample = msaa;
  5485. _sapp.ios.view.context = _sapp.ios.eagl_ctx;
  5486. _sapp.ios.view.enableSetNeedsDisplay = NO;
  5487. _sapp.ios.view.userInteractionEnabled = YES;
  5488. _sapp.ios.view.multipleTouchEnabled = YES;
  5489. // on GLKView, contentScaleFactor appears to work just fine!
  5490. if (_sapp.desc.high_dpi) {
  5491. _sapp.ios.view.contentScaleFactor = _sapp.dpi_scale;
  5492. } else {
  5493. _sapp.ios.view.contentScaleFactor = 1.0;
  5494. }
  5495. _sapp.ios.view_ctrl = [[GLKViewController alloc] init];
  5496. _sapp.ios.view_ctrl.view = _sapp.ios.view;
  5497. _sapp.ios.view_ctrl.preferredFramesPerSecond = max_fps / _sapp.swap_interval;
  5498. _sapp.ios.window.rootViewController = _sapp.ios.view_ctrl;
  5499. }
  5500. _SOKOL_PRIVATE void _sapp_ios_gles3_discard_state(void) {
  5501. _SAPP_OBJC_RELEASE(_sapp.ios.view_ctrl);
  5502. _SAPP_OBJC_RELEASE(_sapp.ios.eagl_ctx);
  5503. }
  5504. _SOKOL_PRIVATE bool _sapp_ios_gles3_update_framebuffer_dimensions(CGRect screen_rect) {
  5505. _sapp.framebuffer_width = _sapp_roundf_gzero(screen_rect.size.width * _sapp.dpi_scale);
  5506. _sapp.framebuffer_height = _sapp_roundf_gzero(screen_rect.size.height * _sapp.dpi_scale);
  5507. int cur_fb_width = _sapp_roundf_gzero(_sapp.ios.view.drawableWidth);
  5508. int cur_fb_height = _sapp_roundf_gzero(_sapp.ios.view.drawableHeight);
  5509. return (_sapp.framebuffer_width != cur_fb_width) || (_sapp.framebuffer_height != cur_fb_height);
  5510. }
  5511. #endif
  5512. _SOKOL_PRIVATE void _sapp_ios_discard_state(void) {
  5513. // NOTE: it's safe to call [release] on a nil object
  5514. _SAPP_OBJC_RELEASE(_sapp.ios.textfield_dlg);
  5515. _SAPP_OBJC_RELEASE(_sapp.ios.textfield);
  5516. #if defined(SOKOL_METAL)
  5517. _sapp_ios_mtl_discard_state();
  5518. #else
  5519. _sapp_ios_gles3_discard_state();
  5520. #endif
  5521. _SAPP_OBJC_RELEASE(_sapp.ios.view);
  5522. _SAPP_OBJC_RELEASE(_sapp.ios.window);
  5523. }
  5524. _SOKOL_PRIVATE void _sapp_ios_run(const sapp_desc* desc) {
  5525. _sapp_init_state(desc);
  5526. static int argc = 1;
  5527. static char* argv[] = { (char*)"sokol_app" };
  5528. UIApplicationMain(argc, argv, nil, NSStringFromClass([_sapp_app_delegate class]));
  5529. }
  5530. /* iOS entry function */
  5531. #if !defined(SOKOL_NO_ENTRY)
  5532. int main(int argc, char* argv[]) {
  5533. sapp_desc desc = sokol_main(argc, argv);
  5534. _sapp_ios_run(&desc);
  5535. return 0;
  5536. }
  5537. #endif /* SOKOL_NO_ENTRY */
  5538. _SOKOL_PRIVATE void _sapp_ios_app_event(sapp_event_type type) {
  5539. if (_sapp_events_enabled()) {
  5540. _sapp_init_event(type);
  5541. _sapp_call_event(&_sapp.event);
  5542. }
  5543. }
  5544. _SOKOL_PRIVATE void _sapp_tvos_press_event(sapp_event_type type, NSSet<UIPress *>* presses) {
  5545. if (_sapp_events_enabled()) {
  5546. for (UIPress *press in presses) {
  5547. sapp_keycode key = SAPP_KEYCODE_INVALID;
  5548. switch (press.type) {
  5549. case UIPressTypeUpArrow: key = SAPP_KEYCODE_UP; break;
  5550. case UIPressTypeDownArrow: key = SAPP_KEYCODE_DOWN; break;
  5551. case UIPressTypeLeftArrow: key = SAPP_KEYCODE_LEFT; break;
  5552. case UIPressTypeRightArrow: key = SAPP_KEYCODE_RIGHT; break;
  5553. case UIPressTypeSelect: key = SAPP_KEYCODE_ENTER; break;
  5554. case UIPressTypeMenu: key = SAPP_KEYCODE_MENU; break;
  5555. case UIPressTypePlayPause: key = SAPP_KEYCODE_PAUSE; break;
  5556. default: break;
  5557. }
  5558. if (key != SAPP_KEYCODE_INVALID) {
  5559. _sapp_init_event(type);
  5560. _sapp.event.key_code = key;
  5561. _sapp.event.key_repeat = false;
  5562. _sapp.event.modifiers = 0;
  5563. _sapp_call_event(&_sapp.event);
  5564. }
  5565. }
  5566. }
  5567. }
  5568. _SOKOL_PRIVATE void _sapp_ios_touch_event(sapp_event_type type, NSSet<UITouch *>* touches, UIEvent* event) {
  5569. if (_sapp_events_enabled()) {
  5570. _sapp_init_event(type);
  5571. NSEnumerator* enumerator = event.allTouches.objectEnumerator;
  5572. UITouch* ios_touch;
  5573. while ((ios_touch = [enumerator nextObject])) {
  5574. if ((_sapp.event.num_touches + 1) < SAPP_MAX_TOUCHPOINTS) {
  5575. CGPoint ios_pos = [ios_touch locationInView:_sapp.ios.view];
  5576. sapp_touchpoint* cur_point = &_sapp.event.touches[_sapp.event.num_touches++];
  5577. cur_point->identifier = (uintptr_t) ios_touch;
  5578. cur_point->pos_x = ios_pos.x * _sapp.dpi_scale;
  5579. cur_point->pos_y = ios_pos.y * _sapp.dpi_scale;
  5580. cur_point->changed = [touches containsObject:ios_touch];
  5581. }
  5582. }
  5583. if (_sapp.event.num_touches > 0) {
  5584. _sapp_call_event(&_sapp.event);
  5585. }
  5586. }
  5587. }
  5588. _SOKOL_PRIVATE void _sapp_ios_update_dimensions(void) {
  5589. CGRect screen_rect = UIScreen.mainScreen.bounds;
  5590. _sapp.window_width = _sapp_roundf_gzero(screen_rect.size.width);
  5591. _sapp.window_height = _sapp_roundf_gzero(screen_rect.size.height);
  5592. #if defined(SOKOL_METAL)
  5593. bool dim_changed = _sapp_ios_mtl_update_framebuffer_dimensions(screen_rect);
  5594. #else
  5595. bool dim_changed = _sapp_ios_gles3_update_framebuffer_dimensions(screen_rect);
  5596. #endif
  5597. if (dim_changed && !_sapp.first_frame) {
  5598. _sapp_ios_app_event(SAPP_EVENTTYPE_RESIZED);
  5599. }
  5600. }
  5601. _SOKOL_PRIVATE void _sapp_ios_frame(void) {
  5602. _sapp_ios_update_dimensions();
  5603. _sapp_frame();
  5604. }
  5605. _SOKOL_PRIVATE void _sapp_ios_show_keyboard(bool shown) {
  5606. /* if not happened yet, create an invisible text field */
  5607. if (nil == _sapp.ios.textfield) {
  5608. _sapp.ios.textfield_dlg = [[_sapp_textfield_dlg alloc] init];
  5609. _sapp.ios.textfield = [[UITextField alloc] initWithFrame:CGRectMake(10, 10, 100, 50)];
  5610. _sapp.ios.textfield.keyboardType = UIKeyboardTypeDefault;
  5611. _sapp.ios.textfield.returnKeyType = UIReturnKeyDefault;
  5612. _sapp.ios.textfield.autocapitalizationType = UITextAutocapitalizationTypeNone;
  5613. _sapp.ios.textfield.autocorrectionType = UITextAutocorrectionTypeNo;
  5614. _sapp.ios.textfield.spellCheckingType = UITextSpellCheckingTypeNo;
  5615. _sapp.ios.textfield.hidden = YES;
  5616. _sapp.ios.textfield.text = @"x";
  5617. _sapp.ios.textfield.delegate = _sapp.ios.textfield_dlg;
  5618. [_sapp.ios.view_ctrl.view addSubview:_sapp.ios.textfield];
  5619. #if !defined(_SAPP_TVOS)
  5620. [[NSNotificationCenter defaultCenter] addObserver:_sapp.ios.textfield_dlg
  5621. selector:@selector(keyboardWasShown:)
  5622. name:UIKeyboardDidShowNotification object:nil];
  5623. [[NSNotificationCenter defaultCenter] addObserver:_sapp.ios.textfield_dlg
  5624. selector:@selector(keyboardWillBeHidden:)
  5625. name:UIKeyboardWillHideNotification object:nil];
  5626. [[NSNotificationCenter defaultCenter] addObserver:_sapp.ios.textfield_dlg
  5627. selector:@selector(keyboardDidChangeFrame:)
  5628. name:UIKeyboardDidChangeFrameNotification object:nil];
  5629. #endif
  5630. }
  5631. if (shown) {
  5632. // setting the text field as first responder brings up the onscreen keyboard
  5633. [_sapp.ios.textfield becomeFirstResponder];
  5634. } else {
  5635. [_sapp.ios.textfield resignFirstResponder];
  5636. }
  5637. }
  5638. @implementation _sapp_app_delegate
  5639. - (BOOL)application:(UIApplication*)application didFinishLaunchingWithOptions:(NSDictionary*)launchOptions {
  5640. CGRect screen_rect = UIScreen.mainScreen.bounds;
  5641. _sapp.ios.window = [[UIWindow alloc] initWithFrame:screen_rect];
  5642. _sapp.window_width = _sapp_roundf_gzero(screen_rect.size.width);
  5643. _sapp.window_height = _sapp_roundf_gzero(screen_rect.size.height);
  5644. if (_sapp.desc.high_dpi) {
  5645. _sapp.dpi_scale = (float) UIScreen.mainScreen.nativeScale;
  5646. } else {
  5647. _sapp.dpi_scale = 1.0f;
  5648. }
  5649. _sapp.framebuffer_width = _sapp_roundf_gzero(_sapp.window_width * _sapp.dpi_scale);
  5650. _sapp.framebuffer_height = _sapp_roundf_gzero(_sapp.window_height * _sapp.dpi_scale);
  5651. #if defined(SOKOL_METAL)
  5652. _sapp_ios_mtl_init();
  5653. #else
  5654. _sapp_ios_gles3_init(screen_rect);
  5655. #endif
  5656. [_sapp.ios.window makeKeyAndVisible];
  5657. _sapp.valid = true;
  5658. return YES;
  5659. }
  5660. - (void)applicationWillResignActive:(UIApplication *)application {
  5661. if (!_sapp.ios.suspended) {
  5662. _sapp.ios.suspended = true;
  5663. _sapp_ios_app_event(SAPP_EVENTTYPE_SUSPENDED);
  5664. }
  5665. }
  5666. - (void)applicationDidBecomeActive:(UIApplication *)application {
  5667. if (_sapp.ios.suspended) {
  5668. _sapp.ios.suspended = false;
  5669. _sapp_ios_app_event(SAPP_EVENTTYPE_RESUMED);
  5670. }
  5671. }
  5672. /* NOTE: this method will rarely ever be called, iOS application
  5673. which are terminated by the user are usually killed via signal 9
  5674. by the operating system.
  5675. */
  5676. - (void)applicationWillTerminate:(UIApplication *)application {
  5677. _SOKOL_UNUSED(application);
  5678. _sapp_call_cleanup();
  5679. _sapp_ios_discard_state();
  5680. _sapp_discard_state();
  5681. }
  5682. @end
  5683. @implementation _sapp_textfield_dlg
  5684. - (void)keyboardWasShown:(NSNotification*)notif {
  5685. _sapp.onscreen_keyboard_shown = true;
  5686. /* query the keyboard's size, and modify the content view's size */
  5687. #if !defined(_SAPP_TVOS)
  5688. if (_sapp.desc.ios.keyboard_resizes_canvas) {
  5689. NSDictionary* info = notif.userInfo;
  5690. CGFloat kbd_h = [[info objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue].size.height;
  5691. CGRect view_frame = UIScreen.mainScreen.bounds;
  5692. view_frame.size.height -= kbd_h;
  5693. _sapp.ios.view.frame = view_frame;
  5694. }
  5695. #endif
  5696. }
  5697. - (void)keyboardWillBeHidden:(NSNotification*)notif {
  5698. _sapp.onscreen_keyboard_shown = false;
  5699. if (_sapp.desc.ios.keyboard_resizes_canvas) {
  5700. _sapp.ios.view.frame = UIScreen.mainScreen.bounds;
  5701. }
  5702. }
  5703. - (void)keyboardDidChangeFrame:(NSNotification*)notif {
  5704. /* this is for the case when the screen rotation changes while the keyboard is open */
  5705. #if !defined(_SAPP_TVOS)
  5706. if (_sapp.onscreen_keyboard_shown && _sapp.desc.ios.keyboard_resizes_canvas) {
  5707. NSDictionary* info = notif.userInfo;
  5708. CGFloat kbd_h = [[info objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue].size.height;
  5709. CGRect view_frame = UIScreen.mainScreen.bounds;
  5710. view_frame.size.height -= kbd_h;
  5711. _sapp.ios.view.frame = view_frame;
  5712. }
  5713. #endif
  5714. }
  5715. - (BOOL)textField:(UITextField*)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString*)string {
  5716. if (_sapp_events_enabled()) {
  5717. const NSUInteger len = string.length;
  5718. if (len > 0) {
  5719. for (NSUInteger i = 0; i < len; i++) {
  5720. unichar c = [string characterAtIndex:i];
  5721. if (c >= 32) {
  5722. /* ignore surrogates for now */
  5723. if ((c < 0xD800) || (c > 0xDFFF)) {
  5724. _sapp_init_event(SAPP_EVENTTYPE_CHAR);
  5725. _sapp.event.char_code = c;
  5726. _sapp_call_event(&_sapp.event);
  5727. }
  5728. }
  5729. if (c <= 32) {
  5730. sapp_keycode k = SAPP_KEYCODE_INVALID;
  5731. switch (c) {
  5732. case 10: k = SAPP_KEYCODE_ENTER; break;
  5733. case 32: k = SAPP_KEYCODE_SPACE; break;
  5734. default: break;
  5735. }
  5736. if (k != SAPP_KEYCODE_INVALID) {
  5737. _sapp_init_event(SAPP_EVENTTYPE_KEY_DOWN);
  5738. _sapp.event.key_code = k;
  5739. _sapp_call_event(&_sapp.event);
  5740. _sapp_init_event(SAPP_EVENTTYPE_KEY_UP);
  5741. _sapp.event.key_code = k;
  5742. _sapp_call_event(&_sapp.event);
  5743. }
  5744. }
  5745. }
  5746. } else {
  5747. // this was a backspace
  5748. _sapp_init_event(SAPP_EVENTTYPE_KEY_DOWN);
  5749. _sapp.event.key_code = SAPP_KEYCODE_BACKSPACE;
  5750. _sapp_call_event(&_sapp.event);
  5751. _sapp_init_event(SAPP_EVENTTYPE_KEY_UP);
  5752. _sapp.event.key_code = SAPP_KEYCODE_BACKSPACE;
  5753. _sapp_call_event(&_sapp.event);
  5754. }
  5755. }
  5756. return NO;
  5757. }
  5758. @end
  5759. @implementation _sapp_ios_view
  5760. - (void)drawRect:(CGRect)rect {
  5761. _SOKOL_UNUSED(rect);
  5762. #if defined(_SAPP_ANY_GL)
  5763. glGetIntegerv(GL_FRAMEBUFFER_BINDING, (GLint*)&_sapp.gl.framebuffer);
  5764. #endif
  5765. _sapp_timing_measure(&_sapp.timing);
  5766. @autoreleasepool {
  5767. _sapp_ios_frame();
  5768. }
  5769. }
  5770. - (BOOL)isOpaque {
  5771. return YES;
  5772. }
  5773. - (void)pressesBegan:(NSSet<UIPress *> *)presses withEvent:(UIPressesEvent *)event {
  5774. _sapp_tvos_press_event(SAPP_EVENTTYPE_KEY_DOWN, presses);
  5775. }
  5776. - (void)pressesChanged:(NSSet<UIPress *> *)presses withEvent:(UIPressesEvent *)event {
  5777. }
  5778. - (void)pressesEnded:(NSSet<UIPress *> *)presses withEvent:(UIPressesEvent *)event {
  5779. _sapp_tvos_press_event(SAPP_EVENTTYPE_KEY_UP, presses);
  5780. }
  5781. - (void)pressesCancelled:(NSSet<UIPress *> *)presses withEvent:(UIPressesEvent *)event {
  5782. _sapp_tvos_press_event(SAPP_EVENTTYPE_KEY_UP, presses);
  5783. }
  5784. - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent*)event {
  5785. _sapp_ios_touch_event(SAPP_EVENTTYPE_TOUCHES_BEGAN, touches, event);
  5786. }
  5787. - (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(UIEvent*)event {
  5788. _sapp_ios_touch_event(SAPP_EVENTTYPE_TOUCHES_MOVED, touches, event);
  5789. }
  5790. - (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent*)event {
  5791. _sapp_ios_touch_event(SAPP_EVENTTYPE_TOUCHES_ENDED, touches, event);
  5792. }
  5793. - (void)touchesCancelled:(NSSet<UITouch *> *)touches withEvent:(UIEvent*)event {
  5794. _sapp_ios_touch_event(SAPP_EVENTTYPE_TOUCHES_CANCELLED, touches, event);
  5795. }
  5796. @end
  5797. #endif /* TARGET_OS_IPHONE */
  5798. #endif /* _SAPP_APPLE */
  5799. // ███████ ███ ███ ███████ ██████ ██████ ██ ██████ ████████ ███████ ███ ██
  5800. // ██ ████ ████ ██ ██ ██ ██ ██ ██ ██ ██ ██ ████ ██
  5801. // █████ ██ ████ ██ ███████ ██ ██████ ██ ██████ ██ █████ ██ ██ ██
  5802. // ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
  5803. // ███████ ██ ██ ███████ ██████ ██ ██ ██ ██ ██ ███████ ██ ████
  5804. //
  5805. // >>emscripten
  5806. #if defined(_SAPP_EMSCRIPTEN)
  5807. #if defined(EM_JS_DEPS)
  5808. EM_JS_DEPS(sokol_app, "$withStackSave,$stringToUTF8OnStack,$findCanvasEventTarget")
  5809. #endif
  5810. #ifdef __cplusplus
  5811. extern "C" {
  5812. #endif
  5813. typedef void (*_sapp_html5_fetch_callback) (const sapp_html5_fetch_response*);
  5814. EMSCRIPTEN_KEEPALIVE void _sapp_emsc_onpaste(const char* str) {
  5815. if (_sapp.clipboard.enabled) {
  5816. _sapp_strcpy(str, _sapp.clipboard.buffer, (size_t)_sapp.clipboard.buf_size);
  5817. if (_sapp_events_enabled()) {
  5818. _sapp_init_event(SAPP_EVENTTYPE_CLIPBOARD_PASTED);
  5819. _sapp_call_event(&_sapp.event);
  5820. }
  5821. }
  5822. }
  5823. /* https://developer.mozilla.org/en-US/docs/Web/API/WindowEventHandlers/onbeforeunload */
  5824. EMSCRIPTEN_KEEPALIVE int _sapp_html5_get_ask_leave_site(void) {
  5825. return _sapp.html5_ask_leave_site ? 1 : 0;
  5826. }
  5827. EMSCRIPTEN_KEEPALIVE void _sapp_emsc_begin_drop(int num) {
  5828. if (!_sapp.drop.enabled) {
  5829. return;
  5830. }
  5831. if (num < 0) {
  5832. num = 0;
  5833. }
  5834. if (num > _sapp.drop.max_files) {
  5835. num = _sapp.drop.max_files;
  5836. }
  5837. _sapp.drop.num_files = num;
  5838. _sapp_clear_drop_buffer();
  5839. }
  5840. EMSCRIPTEN_KEEPALIVE void _sapp_emsc_drop(int i, const char* name) {
  5841. /* NOTE: name is only the filename part, not a path */
  5842. if (!_sapp.drop.enabled) {
  5843. return;
  5844. }
  5845. if (0 == name) {
  5846. return;
  5847. }
  5848. SOKOL_ASSERT(_sapp.drop.num_files <= _sapp.drop.max_files);
  5849. if ((i < 0) || (i >= _sapp.drop.num_files)) {
  5850. return;
  5851. }
  5852. if (!_sapp_strcpy(name, _sapp_dropped_file_path_ptr(i), (size_t)_sapp.drop.max_path_length)) {
  5853. _SAPP_ERROR(DROPPED_FILE_PATH_TOO_LONG);
  5854. _sapp.drop.num_files = 0;
  5855. }
  5856. }
  5857. EMSCRIPTEN_KEEPALIVE void _sapp_emsc_end_drop(int x, int y, int mods) {
  5858. if (!_sapp.drop.enabled) {
  5859. return;
  5860. }
  5861. if (0 == _sapp.drop.num_files) {
  5862. /* there was an error copying the filenames */
  5863. _sapp_clear_drop_buffer();
  5864. return;
  5865. }
  5866. if (_sapp_events_enabled()) {
  5867. _sapp.mouse.x = (float)x * _sapp.dpi_scale;
  5868. _sapp.mouse.y = (float)y * _sapp.dpi_scale;
  5869. _sapp.mouse.dx = 0.0f;
  5870. _sapp.mouse.dy = 0.0f;
  5871. _sapp_init_event(SAPP_EVENTTYPE_FILES_DROPPED);
  5872. // see sapp_js_add_dragndrop_listeners for mods constants
  5873. if (mods & 1) { _sapp.event.modifiers |= SAPP_MODIFIER_SHIFT; }
  5874. if (mods & 2) { _sapp.event.modifiers |= SAPP_MODIFIER_CTRL; }
  5875. if (mods & 4) { _sapp.event.modifiers |= SAPP_MODIFIER_ALT; }
  5876. if (mods & 8) { _sapp.event.modifiers |= SAPP_MODIFIER_SUPER; }
  5877. _sapp_call_event(&_sapp.event);
  5878. }
  5879. }
  5880. 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) {
  5881. _SAPP_STRUCT(sapp_html5_fetch_response, response);
  5882. response.succeeded = (0 != success);
  5883. response.error_code = (sapp_html5_fetch_error) error_code;
  5884. response.file_index = index;
  5885. response.data.ptr = buf_ptr;
  5886. response.data.size = fetched_size;
  5887. response.buffer.ptr = buf_ptr;
  5888. response.buffer.size = buf_size;
  5889. response.user_data = user_data;
  5890. callback(&response);
  5891. }
  5892. // will be called after the request/exitFullscreen promise rejects
  5893. // to restore the _sapp.fullscreen flag to the actual fullscreen state
  5894. EMSCRIPTEN_KEEPALIVE void _sapp_emsc_set_fullscreen_flag(int f) {
  5895. _sapp.fullscreen = (bool)f;
  5896. }
  5897. #ifdef __cplusplus
  5898. } /* extern "C" */
  5899. #endif
  5900. EM_JS(void, sapp_js_add_beforeunload_listener, (void), {
  5901. Module.sokol_beforeunload = (event) => {
  5902. if (__sapp_html5_get_ask_leave_site() != 0) {
  5903. event.preventDefault();
  5904. event.returnValue = ' ';
  5905. }
  5906. };
  5907. window.addEventListener('beforeunload', Module.sokol_beforeunload);
  5908. })
  5909. EM_JS(void, sapp_js_remove_beforeunload_listener, (void), {
  5910. window.removeEventListener('beforeunload', Module.sokol_beforeunload);
  5911. })
  5912. EM_JS(void, sapp_js_add_clipboard_listener, (void), {
  5913. Module.sokol_paste = (event) => {
  5914. const pasted_str = event.clipboardData.getData('text');
  5915. withStackSave(() => {
  5916. const cstr = stringToUTF8OnStack(pasted_str);
  5917. __sapp_emsc_onpaste(cstr);
  5918. });
  5919. };
  5920. window.addEventListener('paste', Module.sokol_paste);
  5921. })
  5922. EM_JS(void, sapp_js_remove_clipboard_listener, (void), {
  5923. window.removeEventListener('paste', Module.sokol_paste);
  5924. })
  5925. EM_JS(void, sapp_js_write_clipboard, (const char* c_str), {
  5926. const str = UTF8ToString(c_str);
  5927. const ta = document.createElement('textarea');
  5928. ta.setAttribute('autocomplete', 'off');
  5929. ta.setAttribute('autocorrect', 'off');
  5930. ta.setAttribute('autocapitalize', 'off');
  5931. ta.setAttribute('spellcheck', 'false');
  5932. ta.style.left = -100 + 'px';
  5933. ta.style.top = -100 + 'px';
  5934. ta.style.height = 1;
  5935. ta.style.width = 1;
  5936. ta.value = str;
  5937. document.body.appendChild(ta);
  5938. ta.select();
  5939. document.execCommand('copy');
  5940. document.body.removeChild(ta);
  5941. })
  5942. _SOKOL_PRIVATE void _sapp_emsc_set_clipboard_string(const char* str) {
  5943. sapp_js_write_clipboard(str);
  5944. }
  5945. EM_JS(void, sapp_js_add_dragndrop_listeners, (void), {
  5946. Module.sokol_drop_files = [];
  5947. Module.sokol_dragenter = (event) => {
  5948. event.stopPropagation();
  5949. event.preventDefault();
  5950. };
  5951. Module.sokol_dragleave = (event) => {
  5952. event.stopPropagation();
  5953. event.preventDefault();
  5954. };
  5955. Module.sokol_dragover = (event) => {
  5956. event.stopPropagation();
  5957. event.preventDefault();
  5958. };
  5959. Module.sokol_drop = (event) => {
  5960. event.stopPropagation();
  5961. event.preventDefault();
  5962. const files = event.dataTransfer.files;
  5963. Module.sokol_dropped_files = files;
  5964. __sapp_emsc_begin_drop(files.length);
  5965. for (let i = 0; i < files.length; i++) {
  5966. withStackSave(() => {
  5967. const cstr = stringToUTF8OnStack(files[i].name);
  5968. __sapp_emsc_drop(i, cstr);
  5969. });
  5970. }
  5971. let mods = 0;
  5972. if (event.shiftKey) { mods |= 1; }
  5973. if (event.ctrlKey) { mods |= 2; }
  5974. if (event.altKey) { mods |= 4; }
  5975. if (event.metaKey) { mods |= 8; }
  5976. // FIXME? see computation of targetX/targetY in emscripten via getClientBoundingRect
  5977. __sapp_emsc_end_drop(event.clientX, event.clientY, mods);
  5978. };
  5979. \x2F\x2A\x2A @suppress {missingProperties} \x2A\x2F
  5980. const canvas = Module.sapp_emsc_target;
  5981. canvas.addEventListener('dragenter', Module.sokol_dragenter, false);
  5982. canvas.addEventListener('dragleave', Module.sokol_dragleave, false);
  5983. canvas.addEventListener('dragover', Module.sokol_dragover, false);
  5984. canvas.addEventListener('drop', Module.sokol_drop, false);
  5985. })
  5986. EM_JS(uint32_t, sapp_js_dropped_file_size, (int index), {
  5987. \x2F\x2A\x2A @suppress {missingProperties} \x2A\x2F
  5988. const files = Module.sokol_dropped_files;
  5989. if ((index < 0) || (index >= files.length)) {
  5990. return 0;
  5991. } else {
  5992. return files[index].size;
  5993. }
  5994. })
  5995. 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), {
  5996. const reader = new FileReader();
  5997. reader.onload = (loadEvent) => {
  5998. const content = loadEvent.target.result;
  5999. if (content.byteLength > buf_size) {
  6000. // SAPP_HTML5_FETCH_ERROR_BUFFER_TOO_SMALL
  6001. __sapp_emsc_invoke_fetch_cb(index, 0, 1, callback, 0, buf_ptr, buf_size, user_data);
  6002. } else {
  6003. HEAPU8.set(new Uint8Array(content), buf_ptr);
  6004. __sapp_emsc_invoke_fetch_cb(index, 1, 0, callback, content.byteLength, buf_ptr, buf_size, user_data);
  6005. }
  6006. };
  6007. reader.onerror = () => {
  6008. // SAPP_HTML5_FETCH_ERROR_OTHER
  6009. __sapp_emsc_invoke_fetch_cb(index, 0, 2, callback, 0, buf_ptr, buf_size, user_data);
  6010. };
  6011. \x2F\x2A\x2A @suppress {missingProperties} \x2A\x2F
  6012. const files = Module.sokol_dropped_files;
  6013. reader.readAsArrayBuffer(files[index]);
  6014. })
  6015. EM_JS(void, sapp_js_remove_dragndrop_listeners, (void), {
  6016. \x2F\x2A\x2A @suppress {missingProperties} \x2A\x2F
  6017. const canvas = Module.sapp_emsc_target;
  6018. canvas.removeEventListener('dragenter', Module.sokol_dragenter);
  6019. canvas.removeEventListener('dragleave', Module.sokol_dragleave);
  6020. canvas.removeEventListener('dragover', Module.sokol_dragover);
  6021. canvas.removeEventListener('drop', Module.sokol_drop);
  6022. })
  6023. EM_JS(void, sapp_js_init, (const char* c_str_target_selector, const char* c_str_document_title), {
  6024. if (c_str_document_title !== 0) {
  6025. document.title = UTF8ToString(c_str_document_title);
  6026. }
  6027. const target_selector_str = UTF8ToString(c_str_target_selector);
  6028. if (Module['canvas'] !== undefined) {
  6029. if (typeof Module['canvas'] === 'object') {
  6030. specialHTMLTargets[target_selector_str] = Module['canvas'];
  6031. } else {
  6032. console.warn("sokol_app.h: Module['canvas'] is set but is not an object");
  6033. }
  6034. }
  6035. Module.sapp_emsc_target = findCanvasEventTarget(target_selector_str);
  6036. if (!Module.sapp_emsc_target) {
  6037. console.warn("sokol_app.h: can't find html5_canvas_selector ", target_selector_str);
  6038. }
  6039. if (!Module.sapp_emsc_target.requestPointerLock) {
  6040. console.warn("sokol_app.h: target doesn't support requestPointerLock: ", target_selector_str);
  6041. }
  6042. })
  6043. _SOKOL_PRIVATE EM_BOOL _sapp_emsc_pointerlockchange_cb(int emsc_type, const EmscriptenPointerlockChangeEvent* emsc_event, void* user_data) {
  6044. _SOKOL_UNUSED(emsc_type);
  6045. _SOKOL_UNUSED(user_data);
  6046. _sapp.mouse.locked = emsc_event->isActive;
  6047. return EM_TRUE;
  6048. }
  6049. _SOKOL_PRIVATE EM_BOOL _sapp_emsc_pointerlockerror_cb(int emsc_type, const void* reserved, void* user_data) {
  6050. _SOKOL_UNUSED(emsc_type);
  6051. _SOKOL_UNUSED(reserved);
  6052. _SOKOL_UNUSED(user_data);
  6053. _sapp.mouse.locked = false;
  6054. _sapp.emsc.mouse_lock_requested = false;
  6055. return true;
  6056. }
  6057. EM_JS(void, sapp_js_request_pointerlock, (void), {
  6058. if (Module.sapp_emsc_target) {
  6059. if (Module.sapp_emsc_target.requestPointerLock) {
  6060. Module.sapp_emsc_target.requestPointerLock();
  6061. }
  6062. }
  6063. })
  6064. EM_JS(void, sapp_js_exit_pointerlock, (void), {
  6065. if (document.exitPointerLock) {
  6066. document.exitPointerLock();
  6067. }
  6068. })
  6069. _SOKOL_PRIVATE void _sapp_emsc_lock_mouse(bool lock) {
  6070. if (lock) {
  6071. /* request mouse-lock during event handler invocation (see _sapp_emsc_update_mouse_lock_state) */
  6072. _sapp.emsc.mouse_lock_requested = true;
  6073. } else {
  6074. /* NOTE: the _sapp.mouse_locked state will be set in the pointerlockchange callback */
  6075. _sapp.emsc.mouse_lock_requested = false;
  6076. sapp_js_exit_pointerlock();
  6077. }
  6078. }
  6079. /* called from inside event handlers to check if mouse lock had been requested,
  6080. and if yes, actually enter mouse lock.
  6081. */
  6082. _SOKOL_PRIVATE void _sapp_emsc_update_mouse_lock_state(void) {
  6083. if (_sapp.emsc.mouse_lock_requested) {
  6084. _sapp.emsc.mouse_lock_requested = false;
  6085. sapp_js_request_pointerlock();
  6086. }
  6087. }
  6088. // set mouse cursor type
  6089. EM_JS(void, sapp_js_set_cursor, (int cursor_type, int shown, int use_custom_cursor_image), {
  6090. if (Module.sapp_emsc_target) {
  6091. let cursor;
  6092. if (shown === 0) {
  6093. cursor = "none";
  6094. } else if (use_custom_cursor_image != 0) {
  6095. cursor = Module.__sapp_custom_cursors[cursor_type].css_property;
  6096. } else switch (cursor_type) {
  6097. case 0: cursor = "auto"; break; // SAPP_MOUSECURSOR_DEFAULT
  6098. case 1: cursor = "default"; break; // SAPP_MOUSECURSOR_ARROW
  6099. case 2: cursor = "text"; break; // SAPP_MOUSECURSOR_IBEAM
  6100. case 3: cursor = "crosshair"; break; // SAPP_MOUSECURSOR_CROSSHAIR
  6101. case 4: cursor = "pointer"; break; // SAPP_MOUSECURSOR_POINTING_HAND
  6102. case 5: cursor = "ew-resize"; break; // SAPP_MOUSECURSOR_RESIZE_EW
  6103. case 6: cursor = "ns-resize"; break; // SAPP_MOUSECURSOR_RESIZE_NS
  6104. case 7: cursor = "nwse-resize"; break; // SAPP_MOUSECURSOR_RESIZE_NWSE
  6105. case 8: cursor = "nesw-resize"; break; // SAPP_MOUSECURSOR_RESIZE_NESW
  6106. case 9: cursor = "all-scroll"; break; // SAPP_MOUSECURSOR_RESIZE_ALL
  6107. case 10: cursor = "not-allowed"; break; // SAPP_MOUSECURSOR_NOT_ALLOWED
  6108. default: cursor = "auto"; break;
  6109. }
  6110. Module.sapp_emsc_target.style.cursor = cursor;
  6111. }
  6112. })
  6113. _SOKOL_PRIVATE void _sapp_emsc_update_cursor(sapp_mouse_cursor cursor, bool shown) {
  6114. SOKOL_ASSERT((cursor >= 0) && (cursor < _SAPP_MOUSECURSOR_NUM));
  6115. bool custom_cursor = _sapp.custom_cursor_bound[cursor];
  6116. sapp_js_set_cursor((int)cursor, shown ? 1 : 0, custom_cursor ? 1 : 0);
  6117. }
  6118. EM_JS(void, sapp_js_make_custom_mouse_cursor, (int cursor_slot_idx, int width, int height, const void* pixels_ptr, int hotspot_x, int hotspot_y), {
  6119. // encode the cursor pixels into a BMP which then is encoded into an 'object url'
  6120. const bmp_hdr_size = 14;
  6121. const dib_hdr_size = 124; // common values are 56, I saw 124 for the rgba32-1.bmp file of the test suite included in firefox, and 108 from wikipedia example 2 (transparent)
  6122. const pixels_size = width * height * 4;
  6123. const bmp_size = bmp_hdr_size + dib_hdr_size + pixels_size;
  6124. const bmp = new Uint8Array(bmp_size);
  6125. let idx = 0;
  6126. const w8 = (val) => {
  6127. bmp[idx++] = val & 255;
  6128. };
  6129. const w16 = (val) => {
  6130. bmp[idx++] = val & 255;
  6131. bmp[idx++] = (val >> 8) & 255;
  6132. };
  6133. const w32 = (val) => {
  6134. bmp[idx++] = val & 255;
  6135. bmp[idx++] = (val >> 8) & 255;
  6136. bmp[idx++] = (val >> 16) & 255;
  6137. bmp[idx++] = (val >> 24) & 255;
  6138. };
  6139. // bmp file header
  6140. w8(66); // 'B'
  6141. w8(77); // 'M'
  6142. w32(bmp_size);
  6143. w32(0); // reserved
  6144. w32(bmp_hdr_size + dib_hdr_size); // offset to pixel data
  6145. assert(idx == bmp_hdr_size);
  6146. // DIB header
  6147. w32(dib_hdr_size); // header size
  6148. w32(width);
  6149. w32(height);
  6150. w16(1); // planes
  6151. w16(32); // bits per pixel
  6152. w32(3); // compression method. 3 = BI_BITFIELDS
  6153. w32(pixels_size); // image size
  6154. w32(2835); // pixel per metre horizontal
  6155. w32(2835); // pixel per metre vertical
  6156. w32(0); // colors number
  6157. w32(0); // important colors
  6158. w32(0x000000ff); // red channel bit mask (big endian)
  6159. w32(0x0000ff00); // green channel bit mask (big endian)
  6160. w32(0x00ff0000); // blue channel bit mask (big endian)
  6161. w32(0xff000000); // alpha channel bit mask (big endian)
  6162. w8(66); w8(71); w8(82); w8(115); // color space type: 'sRGB'
  6163. idx += 64; // color space stuff, unused for 'Win ' or 'sRGB'
  6164. assert(idx == bmp_hdr_size + dib_hdr_size);
  6165. const row_pitch = width * 4;
  6166. for (let y = 0; y < height; y++) {
  6167. const src_idx = pixels_ptr + y * row_pitch;
  6168. const dst_idx = idx + (height - y - 1) * row_pitch;
  6169. const row_data = HEAPU8.slice(src_idx, src_idx + row_pitch);
  6170. bmp.set(row_data, dst_idx);
  6171. }
  6172. const blob = new Blob([bmp.buffer], { type: 'image/bmp' });
  6173. const url = URL.createObjectURL(blob);
  6174. const cursor_slot = {
  6175. css_property: `url('${url}') ${hotspot_x} ${hotspot_y}, auto`,
  6176. blob_url: url // so we can release it later
  6177. };
  6178. // Store a reference to the js cursor object in a global table, indexed by its sapp_mouse_cursor
  6179. if (!Module.__sapp_custom_cursors) {
  6180. Module.__sapp_custom_cursors = Array().fill(null);
  6181. }
  6182. Module.__sapp_custom_cursors[cursor_slot_idx] = cursor_slot;
  6183. })
  6184. EM_JS(void, sapp_js_destroy_custom_mouse_cursor, (int cursor_slot_idx), {
  6185. if (Module.__sapp_custom_cursors) {
  6186. const cursor = Module.__sapp_custom_cursors[cursor_slot_idx];
  6187. URL.revokeObjectURL(cursor.blob_url); // release the url, which should allow the blob to be garbage collected.
  6188. Module.__sapp_custom_cursors[cursor_slot_idx] = null; // clear this array entry
  6189. }
  6190. })
  6191. _SOKOL_PRIVATE bool _sapp_emsc_make_custom_mouse_cursor(sapp_mouse_cursor cursor, const sapp_image_desc* desc) {
  6192. sapp_js_make_custom_mouse_cursor((int)cursor, desc->width, desc->height, desc->pixels.ptr, desc->cursor_hotspot_x, desc->cursor_hotspot_y);
  6193. return true;
  6194. }
  6195. _SOKOL_PRIVATE void _sapp_emsc_destroy_custom_mouse_cursor(sapp_mouse_cursor cursor) {
  6196. sapp_js_destroy_custom_mouse_cursor((int) cursor);
  6197. }
  6198. // NOTE: this callback is needed to react to the user actively leaving fullscreen mode via Esc
  6199. _SOKOL_PRIVATE EM_BOOL _sapp_emsc_fullscreenchange_cb(int emsc_type, const EmscriptenFullscreenChangeEvent* emsc_event, void* user_data) {
  6200. _SOKOL_UNUSED(emsc_type);
  6201. _SOKOL_UNUSED(user_data);
  6202. _sapp.fullscreen = emsc_event->isFullscreen;
  6203. return true;
  6204. }
  6205. EM_JS(void, sapp_js_toggle_fullscreen, (void), {
  6206. const canvas = Module.sapp_emsc_target;
  6207. if (canvas) {
  6208. // NOTE: Safari had the prefix until 2023, Firefox until 2018
  6209. const fullscreenElement = document.fullscreenElement || document.webkitFullscreenElement || document.mozFullScreenElement;
  6210. let p = undefined;
  6211. if (!fullscreenElement) {
  6212. if (canvas.requestFullscreen) {
  6213. p = canvas.requestFullscreen();
  6214. } else if (canvas.webkitRequestFullscreen) {
  6215. p = canvas.webkitRequestFullscreen();
  6216. } else if (canvas.mozRequestFullScreen) {
  6217. p = canvas.mozRequestFullScreen();
  6218. }
  6219. if (p) {
  6220. p.catch((err) => {
  6221. console.warn('sapp_js_toggle_fullscreen(): failed to enter fullscreen mode with', err);
  6222. __sapp_emsc_set_fullscreen_flag(0);
  6223. });
  6224. } else {
  6225. console.warn('sapp_js_toogle_fullscreen(): browser has no [webkit|moz]requestFullscreen function');
  6226. __sapp_emsc_set_fullscreen_flag(0);
  6227. }
  6228. } else {
  6229. if (document.exitFullscreen) {
  6230. p = document.exitFullscreen();
  6231. } else if (document.webkitExitFullscreen) {
  6232. p = document.webkitExitFullscreen();
  6233. } else if (document.mozCancelFullScreen) {
  6234. p = document.mozCancelFullScreen();
  6235. }
  6236. if (p) {
  6237. p.catch((err) => {
  6238. console.warn('sapp_js_toggle_fullscreen(): failed to exit fullscreen mode with', err);
  6239. __sapp_emsc_set_fullscreen_flag(1);
  6240. });
  6241. } else {
  6242. console.warn('sapp_js_toggle_fullscreen(): browser has no [wekbit|moz]exitFullscreen');
  6243. // NOTE: don't need to explicitly set the fullscreen flag here
  6244. }
  6245. }
  6246. }
  6247. })
  6248. _SOKOL_PRIVATE void _sapp_emsc_toggle_fullscreen(void) {
  6249. // toggle the fullscreen flag preliminary, this may be undone
  6250. // when requesting/exiting fullscreen mode actually fails
  6251. _sapp.fullscreen = !_sapp.fullscreen;
  6252. sapp_js_toggle_fullscreen();
  6253. }
  6254. /* JS helper functions to update browser tab favicon */
  6255. EM_JS(void, sapp_js_clear_favicon, (void), {
  6256. const link = document.getElementById('sokol-app-favicon');
  6257. if (link) {
  6258. document.head.removeChild(link);
  6259. }
  6260. })
  6261. EM_JS(void, sapp_js_set_favicon, (int w, int h, const uint8_t* pixels), {
  6262. const canvas = document.createElement('canvas');
  6263. canvas.width = w;
  6264. canvas.height = h;
  6265. const ctx = canvas.getContext('2d');
  6266. const img_data = ctx.createImageData(w, h);
  6267. img_data.data.set(HEAPU8.subarray(pixels, pixels + w*h*4));
  6268. ctx.putImageData(img_data, 0, 0);
  6269. const new_link = document.createElement('link');
  6270. new_link.id = 'sokol-app-favicon';
  6271. new_link.rel = 'shortcut icon';
  6272. new_link.href = canvas.toDataURL();
  6273. document.head.appendChild(new_link);
  6274. })
  6275. _SOKOL_PRIVATE void _sapp_emsc_set_icon(const sapp_icon_desc* icon_desc, int num_images) {
  6276. SOKOL_ASSERT((num_images > 0) && (num_images <= SAPP_MAX_ICONIMAGES));
  6277. sapp_js_clear_favicon();
  6278. // find the best matching image candidate for 16x16 pixels
  6279. int img_index = _sapp_image_bestmatch(icon_desc->images, num_images, 16, 16);
  6280. const sapp_image_desc* img_desc = &icon_desc->images[img_index];
  6281. sapp_js_set_favicon(img_desc->width, img_desc->height, (const uint8_t*) img_desc->pixels.ptr);
  6282. }
  6283. _SOKOL_PRIVATE uint32_t _sapp_emsc_mouse_button_mods(uint16_t buttons) {
  6284. uint32_t m = 0;
  6285. if (0 != (buttons & (1<<0))) { m |= SAPP_MODIFIER_LMB; }
  6286. if (0 != (buttons & (1<<1))) { m |= SAPP_MODIFIER_RMB; } // not a bug
  6287. if (0 != (buttons & (1<<2))) { m |= SAPP_MODIFIER_MMB; } // not a bug
  6288. return m;
  6289. }
  6290. _SOKOL_PRIVATE uint32_t _sapp_emsc_mouse_event_mods(const EmscriptenMouseEvent* ev) {
  6291. uint32_t m = 0;
  6292. if (ev->ctrlKey) { m |= SAPP_MODIFIER_CTRL; }
  6293. if (ev->shiftKey) { m |= SAPP_MODIFIER_SHIFT; }
  6294. if (ev->altKey) { m |= SAPP_MODIFIER_ALT; }
  6295. if (ev->metaKey) { m |= SAPP_MODIFIER_SUPER; }
  6296. m |= _sapp_emsc_mouse_button_mods(_sapp.emsc.mouse_buttons);
  6297. return m;
  6298. }
  6299. _SOKOL_PRIVATE uint32_t _sapp_emsc_key_event_mods(const EmscriptenKeyboardEvent* ev) {
  6300. uint32_t m = 0;
  6301. if (ev->ctrlKey) { m |= SAPP_MODIFIER_CTRL; }
  6302. if (ev->shiftKey) { m |= SAPP_MODIFIER_SHIFT; }
  6303. if (ev->altKey) { m |= SAPP_MODIFIER_ALT; }
  6304. if (ev->metaKey) { m |= SAPP_MODIFIER_SUPER; }
  6305. m |= _sapp_emsc_mouse_button_mods(_sapp.emsc.mouse_buttons);
  6306. return m;
  6307. }
  6308. _SOKOL_PRIVATE uint32_t _sapp_emsc_touch_event_mods(const EmscriptenTouchEvent* ev) {
  6309. uint32_t m = 0;
  6310. if (ev->ctrlKey) { m |= SAPP_MODIFIER_CTRL; }
  6311. if (ev->shiftKey) { m |= SAPP_MODIFIER_SHIFT; }
  6312. if (ev->altKey) { m |= SAPP_MODIFIER_ALT; }
  6313. if (ev->metaKey) { m |= SAPP_MODIFIER_SUPER; }
  6314. m |= _sapp_emsc_mouse_button_mods(_sapp.emsc.mouse_buttons);
  6315. return m;
  6316. }
  6317. _SOKOL_PRIVATE EM_BOOL _sapp_emsc_size_changed(int event_type, const EmscriptenUiEvent* ui_event, void* user_data) {
  6318. _SOKOL_UNUSED(event_type);
  6319. _SOKOL_UNUSED(user_data);
  6320. double w, h;
  6321. emscripten_get_element_css_size(_sapp.html5_canvas_selector, &w, &h);
  6322. /* The above method might report zero when toggling HTML5 fullscreen,
  6323. in that case use the window's inner width reported by the
  6324. emscripten event. This works ok when toggling *into* fullscreen
  6325. but doesn't properly restore the previous canvas size when switching
  6326. back from fullscreen.
  6327. In general, due to the HTML5's fullscreen API's flaky nature it is
  6328. recommended to use 'soft fullscreen' (stretching the WebGL canvas
  6329. over the browser windows client rect) with a CSS definition like this:
  6330. position: absolute;
  6331. top: 0px;
  6332. left: 0px;
  6333. margin: 0px;
  6334. border: 0;
  6335. width: 100%;
  6336. height: 100%;
  6337. overflow: hidden;
  6338. display: block;
  6339. */
  6340. if (w < 1.0) {
  6341. w = ui_event->windowInnerWidth;
  6342. } else {
  6343. _sapp.window_width = _sapp_roundf_gzero(w);
  6344. }
  6345. if (h < 1.0) {
  6346. h = ui_event->windowInnerHeight;
  6347. } else {
  6348. _sapp.window_height = _sapp_roundf_gzero(h);
  6349. }
  6350. if (_sapp.desc.high_dpi) {
  6351. _sapp.dpi_scale = emscripten_get_device_pixel_ratio();
  6352. }
  6353. _sapp.framebuffer_width = _sapp_roundf_gzero(w * _sapp.dpi_scale);
  6354. _sapp.framebuffer_height = _sapp_roundf_gzero(h * _sapp.dpi_scale);
  6355. emscripten_set_canvas_element_size(_sapp.html5_canvas_selector, _sapp.framebuffer_width, _sapp.framebuffer_height);
  6356. #if defined(SOKOL_WGPU)
  6357. // on WebGPU: recreate size-dependent rendering surfaces
  6358. _sapp_wgpu_swapchain_size_changed();
  6359. #endif
  6360. if (_sapp_events_enabled()) {
  6361. _sapp_init_event(SAPP_EVENTTYPE_RESIZED);
  6362. _sapp_call_event(&_sapp.event);
  6363. }
  6364. return true;
  6365. }
  6366. _SOKOL_PRIVATE EM_BOOL _sapp_emsc_mouse_cb(int emsc_type, const EmscriptenMouseEvent* emsc_event, void* user_data) {
  6367. _SOKOL_UNUSED(user_data);
  6368. bool consume_event = !_sapp.desc.html5.bubble_mouse_events;
  6369. _sapp.emsc.mouse_buttons = emsc_event->buttons;
  6370. if (_sapp.mouse.locked) {
  6371. _sapp.mouse.dx = (float) emsc_event->movementX;
  6372. _sapp.mouse.dy = (float) emsc_event->movementY;
  6373. } else {
  6374. float new_x = emsc_event->targetX * _sapp.dpi_scale;
  6375. float new_y = emsc_event->targetY * _sapp.dpi_scale;
  6376. if (_sapp.mouse.pos_valid) {
  6377. _sapp.mouse.dx = new_x - _sapp.mouse.x;
  6378. _sapp.mouse.dy = new_y - _sapp.mouse.y;
  6379. }
  6380. _sapp.mouse.x = new_x;
  6381. _sapp.mouse.y = new_y;
  6382. _sapp.mouse.pos_valid = true;
  6383. }
  6384. if (_sapp_events_enabled() && (emsc_event->button >= 0) && (emsc_event->button < SAPP_MAX_MOUSEBUTTONS)) {
  6385. sapp_event_type type;
  6386. bool is_button_event = false;
  6387. bool clear_dxdy = false;
  6388. switch (emsc_type) {
  6389. case EMSCRIPTEN_EVENT_MOUSEDOWN:
  6390. type = SAPP_EVENTTYPE_MOUSE_DOWN;
  6391. is_button_event = true;
  6392. break;
  6393. case EMSCRIPTEN_EVENT_MOUSEUP:
  6394. type = SAPP_EVENTTYPE_MOUSE_UP;
  6395. is_button_event = true;
  6396. break;
  6397. case EMSCRIPTEN_EVENT_MOUSEMOVE:
  6398. type = SAPP_EVENTTYPE_MOUSE_MOVE;
  6399. break;
  6400. case EMSCRIPTEN_EVENT_MOUSEENTER:
  6401. type = SAPP_EVENTTYPE_MOUSE_ENTER;
  6402. clear_dxdy = true;
  6403. break;
  6404. case EMSCRIPTEN_EVENT_MOUSELEAVE:
  6405. type = SAPP_EVENTTYPE_MOUSE_LEAVE;
  6406. clear_dxdy = true;
  6407. break;
  6408. default:
  6409. type = SAPP_EVENTTYPE_INVALID;
  6410. break;
  6411. }
  6412. if (clear_dxdy) {
  6413. _sapp.mouse.dx = 0.0f;
  6414. _sapp.mouse.dy = 0.0f;
  6415. }
  6416. if (type != SAPP_EVENTTYPE_INVALID) {
  6417. _sapp_init_event(type);
  6418. _sapp.event.modifiers = _sapp_emsc_mouse_event_mods(emsc_event);
  6419. if (is_button_event) {
  6420. switch (emsc_event->button) {
  6421. case 0: _sapp.event.mouse_button = SAPP_MOUSEBUTTON_LEFT; break;
  6422. case 1: _sapp.event.mouse_button = SAPP_MOUSEBUTTON_MIDDLE; break;
  6423. case 2: _sapp.event.mouse_button = SAPP_MOUSEBUTTON_RIGHT; break;
  6424. default: _sapp.event.mouse_button = (sapp_mousebutton)emsc_event->button; break;
  6425. }
  6426. } else {
  6427. _sapp.event.mouse_button = SAPP_MOUSEBUTTON_INVALID;
  6428. }
  6429. consume_event |= _sapp_call_event(&_sapp.event);
  6430. }
  6431. // mouse lock can only be activated in mouse button events (not in move, enter or leave)
  6432. if (is_button_event) {
  6433. _sapp_emsc_update_mouse_lock_state();
  6434. }
  6435. }
  6436. return consume_event;
  6437. }
  6438. _SOKOL_PRIVATE EM_BOOL _sapp_emsc_wheel_cb(int emsc_type, const EmscriptenWheelEvent* emsc_event, void* user_data) {
  6439. _SOKOL_UNUSED(emsc_type);
  6440. _SOKOL_UNUSED(user_data);
  6441. bool consume_event = !_sapp.desc.html5.bubble_wheel_events;
  6442. _sapp.emsc.mouse_buttons = emsc_event->mouse.buttons;
  6443. if (_sapp_events_enabled()) {
  6444. _sapp_init_event(SAPP_EVENTTYPE_MOUSE_SCROLL);
  6445. _sapp.event.modifiers = _sapp_emsc_mouse_event_mods(&emsc_event->mouse);
  6446. /* see https://github.com/floooh/sokol/issues/339 */
  6447. float scale;
  6448. switch (emsc_event->deltaMode) {
  6449. case DOM_DELTA_PIXEL: scale = -0.04f; break;
  6450. case DOM_DELTA_LINE: scale = -1.33f; break;
  6451. case DOM_DELTA_PAGE: scale = -10.0f; break; // FIXME: this is a guess
  6452. default: scale = -0.1f; break; // shouldn't happen
  6453. }
  6454. _sapp.event.scroll_x = scale * (float)emsc_event->deltaX;
  6455. _sapp.event.scroll_y = scale * (float)emsc_event->deltaY;
  6456. consume_event |= _sapp_call_event(&_sapp.event);
  6457. }
  6458. _sapp_emsc_update_mouse_lock_state();
  6459. return consume_event;
  6460. }
  6461. static struct {
  6462. const char* str;
  6463. sapp_keycode code;
  6464. } _sapp_emsc_keymap[] = {
  6465. { "Backspace", SAPP_KEYCODE_BACKSPACE },
  6466. { "Tab", SAPP_KEYCODE_TAB },
  6467. { "Enter", SAPP_KEYCODE_ENTER },
  6468. { "ShiftLeft", SAPP_KEYCODE_LEFT_SHIFT },
  6469. { "ShiftRight", SAPP_KEYCODE_RIGHT_SHIFT },
  6470. { "ControlLeft", SAPP_KEYCODE_LEFT_CONTROL },
  6471. { "ControlRight", SAPP_KEYCODE_RIGHT_CONTROL },
  6472. { "AltLeft", SAPP_KEYCODE_LEFT_ALT },
  6473. { "AltRight", SAPP_KEYCODE_RIGHT_ALT },
  6474. { "Pause", SAPP_KEYCODE_PAUSE },
  6475. { "CapsLock", SAPP_KEYCODE_CAPS_LOCK },
  6476. { "Escape", SAPP_KEYCODE_ESCAPE },
  6477. { "Space", SAPP_KEYCODE_SPACE },
  6478. { "PageUp", SAPP_KEYCODE_PAGE_UP },
  6479. { "PageDown", SAPP_KEYCODE_PAGE_DOWN },
  6480. { "End", SAPP_KEYCODE_END },
  6481. { "Home", SAPP_KEYCODE_HOME },
  6482. { "ArrowLeft", SAPP_KEYCODE_LEFT },
  6483. { "ArrowUp", SAPP_KEYCODE_UP },
  6484. { "ArrowRight", SAPP_KEYCODE_RIGHT },
  6485. { "ArrowDown", SAPP_KEYCODE_DOWN },
  6486. { "PrintScreen", SAPP_KEYCODE_PRINT_SCREEN },
  6487. { "Insert", SAPP_KEYCODE_INSERT },
  6488. { "Delete", SAPP_KEYCODE_DELETE },
  6489. { "Digit0", SAPP_KEYCODE_0 },
  6490. { "Digit1", SAPP_KEYCODE_1 },
  6491. { "Digit2", SAPP_KEYCODE_2 },
  6492. { "Digit3", SAPP_KEYCODE_3 },
  6493. { "Digit4", SAPP_KEYCODE_4 },
  6494. { "Digit5", SAPP_KEYCODE_5 },
  6495. { "Digit6", SAPP_KEYCODE_6 },
  6496. { "Digit7", SAPP_KEYCODE_7 },
  6497. { "Digit8", SAPP_KEYCODE_8 },
  6498. { "Digit9", SAPP_KEYCODE_9 },
  6499. { "KeyA", SAPP_KEYCODE_A },
  6500. { "KeyB", SAPP_KEYCODE_B },
  6501. { "KeyC", SAPP_KEYCODE_C },
  6502. { "KeyD", SAPP_KEYCODE_D },
  6503. { "KeyE", SAPP_KEYCODE_E },
  6504. { "KeyF", SAPP_KEYCODE_F },
  6505. { "KeyG", SAPP_KEYCODE_G },
  6506. { "KeyH", SAPP_KEYCODE_H },
  6507. { "KeyI", SAPP_KEYCODE_I },
  6508. { "KeyJ", SAPP_KEYCODE_J },
  6509. { "KeyK", SAPP_KEYCODE_K },
  6510. { "KeyL", SAPP_KEYCODE_L },
  6511. { "KeyM", SAPP_KEYCODE_M },
  6512. { "KeyN", SAPP_KEYCODE_N },
  6513. { "KeyO", SAPP_KEYCODE_O },
  6514. { "KeyP", SAPP_KEYCODE_P },
  6515. { "KeyQ", SAPP_KEYCODE_Q },
  6516. { "KeyR", SAPP_KEYCODE_R },
  6517. { "KeyS", SAPP_KEYCODE_S },
  6518. { "KeyT", SAPP_KEYCODE_T },
  6519. { "KeyU", SAPP_KEYCODE_U },
  6520. { "KeyV", SAPP_KEYCODE_V },
  6521. { "KeyW", SAPP_KEYCODE_W },
  6522. { "KeyX", SAPP_KEYCODE_X },
  6523. { "KeyY", SAPP_KEYCODE_Y },
  6524. { "KeyZ", SAPP_KEYCODE_Z },
  6525. { "MetaLeft", SAPP_KEYCODE_LEFT_SUPER },
  6526. { "MetaRight", SAPP_KEYCODE_RIGHT_SUPER },
  6527. { "Numpad0", SAPP_KEYCODE_KP_0 },
  6528. { "Numpad1", SAPP_KEYCODE_KP_1 },
  6529. { "Numpad2", SAPP_KEYCODE_KP_2 },
  6530. { "Numpad3", SAPP_KEYCODE_KP_3 },
  6531. { "Numpad4", SAPP_KEYCODE_KP_4 },
  6532. { "Numpad5", SAPP_KEYCODE_KP_5 },
  6533. { "Numpad6", SAPP_KEYCODE_KP_6 },
  6534. { "Numpad7", SAPP_KEYCODE_KP_7 },
  6535. { "Numpad8", SAPP_KEYCODE_KP_8 },
  6536. { "Numpad9", SAPP_KEYCODE_KP_9 },
  6537. { "NumpadMultiply", SAPP_KEYCODE_KP_MULTIPLY },
  6538. { "NumpadAdd", SAPP_KEYCODE_KP_ADD },
  6539. { "NumpadSubtract", SAPP_KEYCODE_KP_SUBTRACT },
  6540. { "NumpadDecimal", SAPP_KEYCODE_KP_DECIMAL },
  6541. { "NumpadDivide", SAPP_KEYCODE_KP_DIVIDE },
  6542. { "F1", SAPP_KEYCODE_F1 },
  6543. { "F2", SAPP_KEYCODE_F2 },
  6544. { "F3", SAPP_KEYCODE_F3 },
  6545. { "F4", SAPP_KEYCODE_F4 },
  6546. { "F5", SAPP_KEYCODE_F5 },
  6547. { "F6", SAPP_KEYCODE_F6 },
  6548. { "F7", SAPP_KEYCODE_F7 },
  6549. { "F8", SAPP_KEYCODE_F8 },
  6550. { "F9", SAPP_KEYCODE_F9 },
  6551. { "F10", SAPP_KEYCODE_F10 },
  6552. { "F11", SAPP_KEYCODE_F11 },
  6553. { "F12", SAPP_KEYCODE_F12 },
  6554. { "NumLock", SAPP_KEYCODE_NUM_LOCK },
  6555. { "ScrollLock", SAPP_KEYCODE_SCROLL_LOCK },
  6556. { "Semicolon", SAPP_KEYCODE_SEMICOLON },
  6557. { "Equal", SAPP_KEYCODE_EQUAL },
  6558. { "Comma", SAPP_KEYCODE_COMMA },
  6559. { "Minus", SAPP_KEYCODE_MINUS },
  6560. { "Period", SAPP_KEYCODE_PERIOD },
  6561. { "Slash", SAPP_KEYCODE_SLASH },
  6562. { "Backquote", SAPP_KEYCODE_GRAVE_ACCENT },
  6563. { "BracketLeft", SAPP_KEYCODE_LEFT_BRACKET },
  6564. { "Backslash", SAPP_KEYCODE_BACKSLASH },
  6565. { "BracketRight", SAPP_KEYCODE_RIGHT_BRACKET },
  6566. { "Quote", SAPP_KEYCODE_GRAVE_ACCENT }, // FIXME: ???
  6567. { 0, SAPP_KEYCODE_INVALID },
  6568. };
  6569. _SOKOL_PRIVATE sapp_keycode _sapp_emsc_translate_key(const char* str) {
  6570. int i = 0;
  6571. const char* keystr;
  6572. while (( keystr = _sapp_emsc_keymap[i].str )) {
  6573. if (0 == strcmp(str, keystr)) {
  6574. return _sapp_emsc_keymap[i].code;
  6575. }
  6576. i += 1;
  6577. }
  6578. return SAPP_KEYCODE_INVALID;
  6579. }
  6580. // returns true if the key code is a 'character key', this is used to decide
  6581. // if a key event needs to bubble up to create a char event
  6582. _SOKOL_PRIVATE bool _sapp_emsc_is_char_key(sapp_keycode key_code) {
  6583. return key_code < SAPP_KEYCODE_WORLD_1;
  6584. }
  6585. _SOKOL_PRIVATE EM_BOOL _sapp_emsc_key_cb(int emsc_type, const EmscriptenKeyboardEvent* emsc_event, void* user_data) {
  6586. _SOKOL_UNUSED(user_data);
  6587. bool consume_event = false;
  6588. if (_sapp_events_enabled()) {
  6589. sapp_event_type type;
  6590. switch (emsc_type) {
  6591. case EMSCRIPTEN_EVENT_KEYDOWN:
  6592. type = SAPP_EVENTTYPE_KEY_DOWN;
  6593. break;
  6594. case EMSCRIPTEN_EVENT_KEYUP:
  6595. type = SAPP_EVENTTYPE_KEY_UP;
  6596. break;
  6597. case EMSCRIPTEN_EVENT_KEYPRESS:
  6598. type = SAPP_EVENTTYPE_CHAR;
  6599. break;
  6600. default:
  6601. type = SAPP_EVENTTYPE_INVALID;
  6602. break;
  6603. }
  6604. if (type != SAPP_EVENTTYPE_INVALID) {
  6605. bool send_keyup_followup = false;
  6606. _sapp_init_event(type);
  6607. _sapp.event.key_repeat = emsc_event->repeat;
  6608. _sapp.event.modifiers = _sapp_emsc_key_event_mods(emsc_event);
  6609. if (type == SAPP_EVENTTYPE_CHAR) {
  6610. // NOTE: charCode doesn't appear to be supported on Android Chrome
  6611. _sapp.event.char_code = emsc_event->charCode;
  6612. consume_event |= !_sapp.desc.html5.bubble_char_events;
  6613. } else {
  6614. if (0 != emsc_event->code[0]) {
  6615. // This code path is for desktop browsers which send untranslated 'physical' key code strings
  6616. // (which is what we actually want for key events)
  6617. _sapp.event.key_code = _sapp_emsc_translate_key(emsc_event->code);
  6618. } else {
  6619. // This code path is for mobile browsers which only send localized key code
  6620. // strings. Note that the translation will only work for a small subset
  6621. // of localization-agnostic keys (like Enter, arrow keys, etc...), but
  6622. // regular alpha-numeric keys will all result in an SAPP_KEYCODE_INVALID)
  6623. _sapp.event.key_code = _sapp_emsc_translate_key(emsc_event->key);
  6624. }
  6625. // Special hack for macOS: if the Super key is pressed, macOS doesn't
  6626. // send keyUp events. As a workaround, to prevent keys from
  6627. // "sticking", we'll send a keyup event following a keydown
  6628. // when the SUPER key is pressed
  6629. if ((type == SAPP_EVENTTYPE_KEY_DOWN) &&
  6630. (_sapp.event.key_code != SAPP_KEYCODE_LEFT_SUPER) &&
  6631. (_sapp.event.key_code != SAPP_KEYCODE_RIGHT_SUPER) &&
  6632. (_sapp.event.modifiers & SAPP_MODIFIER_SUPER))
  6633. {
  6634. send_keyup_followup = true;
  6635. }
  6636. // 'character key events' will always need to bubble up, otherwise the browser
  6637. // wouldn't be able to generate character events.
  6638. if (!_sapp_emsc_is_char_key(_sapp.event.key_code)) {
  6639. consume_event |= !_sapp.desc.html5.bubble_key_events;
  6640. }
  6641. }
  6642. consume_event |= _sapp_call_event(&_sapp.event);
  6643. if (send_keyup_followup) {
  6644. _sapp.event.type = SAPP_EVENTTYPE_KEY_UP;
  6645. consume_event |= _sapp_call_event(&_sapp.event);
  6646. }
  6647. }
  6648. }
  6649. _sapp_emsc_update_mouse_lock_state();
  6650. return consume_event;
  6651. }
  6652. _SOKOL_PRIVATE EM_BOOL _sapp_emsc_touch_cb(int emsc_type, const EmscriptenTouchEvent* emsc_event, void* user_data) {
  6653. _SOKOL_UNUSED(user_data);
  6654. bool consume_event = !_sapp.desc.html5.bubble_touch_events;
  6655. if (_sapp_events_enabled()) {
  6656. sapp_event_type type;
  6657. switch (emsc_type) {
  6658. case EMSCRIPTEN_EVENT_TOUCHSTART:
  6659. type = SAPP_EVENTTYPE_TOUCHES_BEGAN;
  6660. break;
  6661. case EMSCRIPTEN_EVENT_TOUCHMOVE:
  6662. type = SAPP_EVENTTYPE_TOUCHES_MOVED;
  6663. break;
  6664. case EMSCRIPTEN_EVENT_TOUCHEND:
  6665. type = SAPP_EVENTTYPE_TOUCHES_ENDED;
  6666. break;
  6667. case EMSCRIPTEN_EVENT_TOUCHCANCEL:
  6668. type = SAPP_EVENTTYPE_TOUCHES_CANCELLED;
  6669. break;
  6670. default:
  6671. type = SAPP_EVENTTYPE_INVALID;
  6672. break;
  6673. }
  6674. if (type != SAPP_EVENTTYPE_INVALID) {
  6675. _sapp_init_event(type);
  6676. _sapp.event.modifiers = _sapp_emsc_touch_event_mods(emsc_event);
  6677. _sapp.event.num_touches = emsc_event->numTouches;
  6678. if (_sapp.event.num_touches > SAPP_MAX_TOUCHPOINTS) {
  6679. _sapp.event.num_touches = SAPP_MAX_TOUCHPOINTS;
  6680. }
  6681. for (int i = 0; i < _sapp.event.num_touches; i++) {
  6682. const EmscriptenTouchPoint* src = &emsc_event->touches[i];
  6683. sapp_touchpoint* dst = &_sapp.event.touches[i];
  6684. dst->identifier = (uintptr_t)src->identifier;
  6685. dst->pos_x = src->targetX * _sapp.dpi_scale;
  6686. dst->pos_y = src->targetY * _sapp.dpi_scale;
  6687. dst->changed = src->isChanged;
  6688. }
  6689. consume_event |= _sapp_call_event(&_sapp.event);
  6690. }
  6691. }
  6692. return consume_event;
  6693. }
  6694. _SOKOL_PRIVATE EM_BOOL _sapp_emsc_focus_cb(int emsc_type, const EmscriptenFocusEvent* emsc_event, void* user_data) {
  6695. _SOKOL_UNUSED(emsc_type);
  6696. _SOKOL_UNUSED(emsc_event);
  6697. _SOKOL_UNUSED(user_data);
  6698. if (_sapp_events_enabled()) {
  6699. _sapp_init_event(SAPP_EVENTTYPE_FOCUSED);
  6700. _sapp_call_event(&_sapp.event);
  6701. }
  6702. return true;
  6703. }
  6704. _SOKOL_PRIVATE EM_BOOL _sapp_emsc_blur_cb(int emsc_type, const EmscriptenFocusEvent* emsc_event, void* user_data) {
  6705. _SOKOL_UNUSED(emsc_type);
  6706. _SOKOL_UNUSED(emsc_event);
  6707. _SOKOL_UNUSED(user_data);
  6708. if (_sapp_events_enabled()) {
  6709. _sapp_init_event(SAPP_EVENTTYPE_UNFOCUSED);
  6710. _sapp_call_event(&_sapp.event);
  6711. }
  6712. return true;
  6713. }
  6714. #if defined(SOKOL_GLES3)
  6715. _SOKOL_PRIVATE EM_BOOL _sapp_emsc_webgl_context_cb(int emsc_type, const void* reserved, void* user_data) {
  6716. _SOKOL_UNUSED(reserved);
  6717. _SOKOL_UNUSED(user_data);
  6718. sapp_event_type type;
  6719. switch (emsc_type) {
  6720. case EMSCRIPTEN_EVENT_WEBGLCONTEXTLOST: type = SAPP_EVENTTYPE_SUSPENDED; break;
  6721. case EMSCRIPTEN_EVENT_WEBGLCONTEXTRESTORED: type = SAPP_EVENTTYPE_RESUMED; break;
  6722. default: type = SAPP_EVENTTYPE_INVALID; break;
  6723. }
  6724. if (_sapp_events_enabled() && (SAPP_EVENTTYPE_INVALID != type)) {
  6725. _sapp_init_event(type);
  6726. _sapp_call_event(&_sapp.event);
  6727. }
  6728. return true;
  6729. }
  6730. _SOKOL_PRIVATE void _sapp_emsc_webgl_init(void) {
  6731. EmscriptenWebGLContextAttributes attrs;
  6732. emscripten_webgl_init_context_attributes(&attrs);
  6733. attrs.alpha = _sapp.desc.alpha;
  6734. attrs.depth = true;
  6735. attrs.stencil = true;
  6736. attrs.antialias = _sapp.sample_count > 1;
  6737. attrs.premultipliedAlpha = _sapp.desc.html5.premultiplied_alpha;
  6738. attrs.preserveDrawingBuffer = _sapp.desc.html5.preserve_drawing_buffer;
  6739. attrs.enableExtensionsByDefault = true;
  6740. attrs.majorVersion = 2;
  6741. EMSCRIPTEN_WEBGL_CONTEXT_HANDLE ctx = emscripten_webgl_create_context(_sapp.html5_canvas_selector, &attrs);
  6742. // FIXME: error message?
  6743. emscripten_webgl_make_context_current(ctx);
  6744. glGetIntegerv(GL_FRAMEBUFFER_BINDING, (GLint*)&_sapp.gl.framebuffer);
  6745. }
  6746. #endif
  6747. _SOKOL_PRIVATE void _sapp_emsc_register_eventhandlers(void) {
  6748. // NOTE: HTML canvas doesn't receive input focus, this is why key event handlers are added
  6749. // to the window object (this could be worked around by adding a "tab index" to the
  6750. // canvas)
  6751. emscripten_set_mousedown_callback(_sapp.html5_canvas_selector, 0, true, _sapp_emsc_mouse_cb);
  6752. emscripten_set_mouseup_callback(_sapp.html5_canvas_selector, 0, true, _sapp_emsc_mouse_cb);
  6753. emscripten_set_mousemove_callback(_sapp.html5_canvas_selector, 0, true, _sapp_emsc_mouse_cb);
  6754. emscripten_set_mouseenter_callback(_sapp.html5_canvas_selector, 0, true, _sapp_emsc_mouse_cb);
  6755. emscripten_set_mouseleave_callback(_sapp.html5_canvas_selector, 0, true, _sapp_emsc_mouse_cb);
  6756. emscripten_set_wheel_callback(_sapp.html5_canvas_selector, 0, true, _sapp_emsc_wheel_cb);
  6757. emscripten_set_keydown_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, 0, true, _sapp_emsc_key_cb);
  6758. emscripten_set_keyup_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, 0, true, _sapp_emsc_key_cb);
  6759. emscripten_set_keypress_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, 0, true, _sapp_emsc_key_cb);
  6760. emscripten_set_touchstart_callback(_sapp.html5_canvas_selector, 0, true, _sapp_emsc_touch_cb);
  6761. emscripten_set_touchmove_callback(_sapp.html5_canvas_selector, 0, true, _sapp_emsc_touch_cb);
  6762. emscripten_set_touchend_callback(_sapp.html5_canvas_selector, 0, true, _sapp_emsc_touch_cb);
  6763. emscripten_set_touchcancel_callback(_sapp.html5_canvas_selector, 0, true, _sapp_emsc_touch_cb);
  6764. emscripten_set_pointerlockchange_callback(EMSCRIPTEN_EVENT_TARGET_DOCUMENT, 0, true, _sapp_emsc_pointerlockchange_cb);
  6765. emscripten_set_pointerlockerror_callback(EMSCRIPTEN_EVENT_TARGET_DOCUMENT, 0, true, _sapp_emsc_pointerlockerror_cb);
  6766. emscripten_set_focus_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, 0, true, _sapp_emsc_focus_cb);
  6767. emscripten_set_blur_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, 0, true, _sapp_emsc_blur_cb);
  6768. emscripten_set_fullscreenchange_callback(_sapp.html5_canvas_selector, 0, true, _sapp_emsc_fullscreenchange_cb);
  6769. sapp_js_add_beforeunload_listener();
  6770. if (_sapp.clipboard.enabled) {
  6771. sapp_js_add_clipboard_listener();
  6772. }
  6773. if (_sapp.drop.enabled) {
  6774. sapp_js_add_dragndrop_listeners();
  6775. }
  6776. #if defined(SOKOL_GLES3)
  6777. emscripten_set_webglcontextlost_callback(_sapp.html5_canvas_selector, 0, true, _sapp_emsc_webgl_context_cb);
  6778. emscripten_set_webglcontextrestored_callback(_sapp.html5_canvas_selector, 0, true, _sapp_emsc_webgl_context_cb);
  6779. #endif
  6780. }
  6781. _SOKOL_PRIVATE void _sapp_emsc_unregister_eventhandlers(void) {
  6782. emscripten_set_mousedown_callback(_sapp.html5_canvas_selector, 0, true, 0);
  6783. emscripten_set_mouseup_callback(_sapp.html5_canvas_selector, 0, true, 0);
  6784. emscripten_set_mousemove_callback(_sapp.html5_canvas_selector, 0, true, 0);
  6785. emscripten_set_mouseenter_callback(_sapp.html5_canvas_selector, 0, true, 0);
  6786. emscripten_set_mouseleave_callback(_sapp.html5_canvas_selector, 0, true, 0);
  6787. emscripten_set_wheel_callback(_sapp.html5_canvas_selector, 0, true, 0);
  6788. emscripten_set_keydown_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, 0, true, 0);
  6789. emscripten_set_keyup_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, 0, true, 0);
  6790. emscripten_set_keypress_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, 0, true, 0);
  6791. emscripten_set_touchstart_callback(_sapp.html5_canvas_selector, 0, true, 0);
  6792. emscripten_set_touchmove_callback(_sapp.html5_canvas_selector, 0, true, 0);
  6793. emscripten_set_touchend_callback(_sapp.html5_canvas_selector, 0, true, 0);
  6794. emscripten_set_touchcancel_callback(_sapp.html5_canvas_selector, 0, true, 0);
  6795. emscripten_set_pointerlockchange_callback(EMSCRIPTEN_EVENT_TARGET_DOCUMENT, 0, true, 0);
  6796. emscripten_set_pointerlockerror_callback(EMSCRIPTEN_EVENT_TARGET_DOCUMENT, 0, true, 0);
  6797. emscripten_set_focus_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, 0, true, 0);
  6798. emscripten_set_blur_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, 0, true, 0);
  6799. emscripten_set_fullscreenchange_callback(_sapp.html5_canvas_selector, 0, true, 0);
  6800. if (!_sapp.desc.html5.canvas_resize) {
  6801. emscripten_set_resize_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, 0, true, 0);
  6802. }
  6803. sapp_js_remove_beforeunload_listener();
  6804. if (_sapp.clipboard.enabled) {
  6805. sapp_js_remove_clipboard_listener();
  6806. }
  6807. if (_sapp.drop.enabled) {
  6808. sapp_js_remove_dragndrop_listeners();
  6809. }
  6810. #if defined(SOKOL_GLES3)
  6811. emscripten_set_webglcontextlost_callback(_sapp.html5_canvas_selector, 0, true, 0);
  6812. emscripten_set_webglcontextrestored_callback(_sapp.html5_canvas_selector, 0, true, 0);
  6813. #endif
  6814. }
  6815. _SOKOL_PRIVATE EM_BOOL _sapp_emsc_frame_animation_loop(double time, void* userData) {
  6816. _SOKOL_UNUSED(userData);
  6817. _sapp_timing_external(&_sapp.timing, time / 1000.0);
  6818. #if defined(SOKOL_WGPU)
  6819. _sapp_wgpu_frame();
  6820. #else
  6821. _sapp_frame();
  6822. #endif
  6823. // quit-handling
  6824. if (_sapp.quit_requested) {
  6825. _sapp_init_event(SAPP_EVENTTYPE_QUIT_REQUESTED);
  6826. _sapp_call_event(&_sapp.event);
  6827. if (_sapp.quit_requested) {
  6828. _sapp.quit_ordered = true;
  6829. }
  6830. }
  6831. if (_sapp.quit_ordered) {
  6832. _sapp_emsc_unregister_eventhandlers();
  6833. #if defined(SOKOL_WGPU)
  6834. _sapp_wgpu_discard();
  6835. #endif
  6836. _sapp_call_cleanup();
  6837. _sapp_discard_state();
  6838. return EM_FALSE;
  6839. }
  6840. return EM_TRUE;
  6841. }
  6842. _SOKOL_PRIVATE void _sapp_emsc_frame_main_loop(void) {
  6843. const double time = emscripten_performance_now();
  6844. if (!_sapp_emsc_frame_animation_loop(time, 0)) {
  6845. emscripten_cancel_main_loop();
  6846. }
  6847. }
  6848. _SOKOL_PRIVATE void _sapp_emsc_run(const sapp_desc* desc) {
  6849. _sapp_init_state(desc);
  6850. _sapp.fullscreen = false; // override user provided fullscreen state: can't start in fullscreen on the web!
  6851. const char* document_title = desc->html5.update_document_title ? _sapp.window_title : 0;
  6852. sapp_js_init(_sapp.html5_canvas_selector, document_title);
  6853. double w, h;
  6854. if (_sapp.desc.html5.canvas_resize) {
  6855. w = (double) _sapp_def(_sapp.desc.width, _SAPP_FALLBACK_DEFAULT_WINDOW_WIDTH);
  6856. h = (double) _sapp_def(_sapp.desc.height, _SAPP_FALLBACK_DEFAULT_WINDOW_HEIGHT);
  6857. } else {
  6858. emscripten_get_element_css_size(_sapp.html5_canvas_selector, &w, &h);
  6859. emscripten_set_resize_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, 0, false, _sapp_emsc_size_changed);
  6860. }
  6861. if (_sapp.desc.high_dpi) {
  6862. _sapp.dpi_scale = emscripten_get_device_pixel_ratio();
  6863. }
  6864. _sapp.window_width = _sapp_roundf_gzero(w);
  6865. _sapp.window_height = _sapp_roundf_gzero(h);
  6866. _sapp.framebuffer_width = _sapp_roundf_gzero(w * _sapp.dpi_scale);
  6867. _sapp.framebuffer_height = _sapp_roundf_gzero(h * _sapp.dpi_scale);
  6868. emscripten_set_canvas_element_size(_sapp.html5_canvas_selector, _sapp.framebuffer_width, _sapp.framebuffer_height);
  6869. #if defined(SOKOL_GLES3)
  6870. _sapp_emsc_webgl_init();
  6871. #elif defined(SOKOL_WGPU)
  6872. _sapp_wgpu_init();
  6873. #endif
  6874. _sapp.valid = true;
  6875. _sapp_emsc_register_eventhandlers();
  6876. sapp_set_icon(&desc->icon);
  6877. // start the frame loop
  6878. if (_sapp.desc.html5.use_emsc_set_main_loop) {
  6879. emscripten_set_main_loop(_sapp_emsc_frame_main_loop, 0, _sapp.desc.html5.emsc_set_main_loop_simulate_infinite_loop);
  6880. } else {
  6881. emscripten_request_animation_frame_loop(_sapp_emsc_frame_animation_loop, 0);
  6882. }
  6883. // NOT A BUG: do not call _sapp_discard_state() here, instead this is
  6884. // called in _sapp_emsc_frame() when the application is ordered to quit
  6885. }
  6886. #if !defined(SOKOL_NO_ENTRY)
  6887. int main(int argc, char* argv[]) {
  6888. sapp_desc desc = sokol_main(argc, argv);
  6889. _sapp_emsc_run(&desc);
  6890. return 0;
  6891. }
  6892. #endif /* SOKOL_NO_ENTRY */
  6893. #endif /* _SAPP_EMSCRIPTEN */
  6894. // ██████ ██ ██ ██ ███████ ██ ██████ ███████ ██████ ███████
  6895. // ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
  6896. // ██ ███ ██ ███████ █████ ██ ██████ █████ ██████ ███████
  6897. // ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
  6898. // ██████ ███████ ██ ██ ███████ ███████ ██ ███████ ██ ██ ███████
  6899. //
  6900. // >>gl helpers
  6901. #if defined(SOKOL_GLCORE)
  6902. typedef struct {
  6903. int red_bits;
  6904. int green_bits;
  6905. int blue_bits;
  6906. int alpha_bits;
  6907. int depth_bits;
  6908. int stencil_bits;
  6909. int samples;
  6910. bool doublebuffer;
  6911. uintptr_t handle;
  6912. } _sapp_gl_fbconfig;
  6913. _SOKOL_PRIVATE void _sapp_gl_init_fbconfig(_sapp_gl_fbconfig* fbconfig) {
  6914. _sapp_clear(fbconfig, sizeof(_sapp_gl_fbconfig));
  6915. /* -1 means "don't care" */
  6916. fbconfig->red_bits = -1;
  6917. fbconfig->green_bits = -1;
  6918. fbconfig->blue_bits = -1;
  6919. fbconfig->alpha_bits = -1;
  6920. fbconfig->depth_bits = -1;
  6921. fbconfig->stencil_bits = -1;
  6922. fbconfig->samples = -1;
  6923. }
  6924. typedef struct {
  6925. int least_missing;
  6926. int least_color_diff;
  6927. int least_extra_diff;
  6928. bool best_match;
  6929. } _sapp_gl_fbselect;
  6930. _SOKOL_PRIVATE void _sapp_gl_init_fbselect(_sapp_gl_fbselect* fbselect) {
  6931. _sapp_clear(fbselect, sizeof(_sapp_gl_fbselect));
  6932. fbselect->least_missing = 1000000;
  6933. fbselect->least_color_diff = 10000000;
  6934. fbselect->least_extra_diff = 10000000;
  6935. fbselect->best_match = false;
  6936. }
  6937. // NOTE: this is used only in the WGL code path
  6938. _SOKOL_PRIVATE bool _sapp_gl_select_fbconfig(_sapp_gl_fbselect* fbselect, const _sapp_gl_fbconfig* desired, const _sapp_gl_fbconfig* current) {
  6939. int missing = 0;
  6940. if (desired->doublebuffer != current->doublebuffer) {
  6941. return false;
  6942. }
  6943. if ((desired->alpha_bits > 0) && (current->alpha_bits == 0)) {
  6944. missing++;
  6945. }
  6946. if ((desired->depth_bits > 0) && (current->depth_bits == 0)) {
  6947. missing++;
  6948. }
  6949. if ((desired->stencil_bits > 0) && (current->stencil_bits == 0)) {
  6950. missing++;
  6951. }
  6952. if ((desired->samples > 0) && (current->samples == 0)) {
  6953. /* Technically, several multisampling buffers could be
  6954. involved, but that's a lower level implementation detail and
  6955. not important to us here, so we count them as one
  6956. */
  6957. missing++;
  6958. }
  6959. /* These polynomials make many small channel size differences matter
  6960. less than one large channel size difference
  6961. Calculate color channel size difference value
  6962. */
  6963. int color_diff = 0;
  6964. if (desired->red_bits != -1) {
  6965. color_diff += (desired->red_bits - current->red_bits) * (desired->red_bits - current->red_bits);
  6966. }
  6967. if (desired->green_bits != -1) {
  6968. color_diff += (desired->green_bits - current->green_bits) * (desired->green_bits - current->green_bits);
  6969. }
  6970. if (desired->blue_bits != -1) {
  6971. color_diff += (desired->blue_bits - current->blue_bits) * (desired->blue_bits - current->blue_bits);
  6972. }
  6973. /* Calculate non-color channel size difference value */
  6974. int extra_diff = 0;
  6975. if (desired->alpha_bits != -1) {
  6976. extra_diff += (desired->alpha_bits - current->alpha_bits) * (desired->alpha_bits - current->alpha_bits);
  6977. }
  6978. if (desired->depth_bits != -1) {
  6979. extra_diff += (desired->depth_bits - current->depth_bits) * (desired->depth_bits - current->depth_bits);
  6980. }
  6981. if (desired->stencil_bits != -1) {
  6982. extra_diff += (desired->stencil_bits - current->stencil_bits) * (desired->stencil_bits - current->stencil_bits);
  6983. }
  6984. if (desired->samples != -1) {
  6985. extra_diff += (desired->samples - current->samples) * (desired->samples - current->samples);
  6986. }
  6987. /* Figure out if the current one is better than the best one found so far
  6988. Least number of missing buffers is the most important heuristic,
  6989. then color buffer size match and lastly size match for other buffers
  6990. */
  6991. bool new_closest = false;
  6992. if (missing < fbselect->least_missing) {
  6993. new_closest = true;
  6994. } else if (missing == fbselect->least_missing) {
  6995. if ((color_diff < fbselect->least_color_diff) ||
  6996. ((color_diff == fbselect->least_color_diff) && (extra_diff < fbselect->least_extra_diff)))
  6997. {
  6998. new_closest = true;
  6999. }
  7000. }
  7001. if (new_closest) {
  7002. fbselect->least_missing = missing;
  7003. fbselect->least_color_diff = color_diff;
  7004. fbselect->least_extra_diff = extra_diff;
  7005. fbselect->best_match = (missing | color_diff | extra_diff) == 0;
  7006. }
  7007. return new_closest;
  7008. }
  7009. // NOTE: this is used only in the GLX code path
  7010. _SOKOL_PRIVATE const _sapp_gl_fbconfig* _sapp_gl_choose_fbconfig(const _sapp_gl_fbconfig* desired, const _sapp_gl_fbconfig* alternatives, int count) {
  7011. int missing, least_missing = 1000000;
  7012. int color_diff, least_color_diff = 10000000;
  7013. int extra_diff, least_extra_diff = 10000000;
  7014. const _sapp_gl_fbconfig* current;
  7015. const _sapp_gl_fbconfig* closest = 0;
  7016. for (int i = 0; i < count; i++) {
  7017. current = alternatives + i;
  7018. if (desired->doublebuffer != current->doublebuffer) {
  7019. continue;
  7020. }
  7021. missing = 0;
  7022. if (desired->alpha_bits > 0 && current->alpha_bits == 0) {
  7023. missing++;
  7024. }
  7025. if (desired->depth_bits > 0 && current->depth_bits == 0) {
  7026. missing++;
  7027. }
  7028. if (desired->stencil_bits > 0 && current->stencil_bits == 0) {
  7029. missing++;
  7030. }
  7031. if (desired->samples > 0 && current->samples == 0) {
  7032. /* Technically, several multisampling buffers could be
  7033. involved, but that's a lower level implementation detail and
  7034. not important to us here, so we count them as one
  7035. */
  7036. missing++;
  7037. }
  7038. /* These polynomials make many small channel size differences matter
  7039. less than one large channel size difference
  7040. Calculate color channel size difference value
  7041. */
  7042. color_diff = 0;
  7043. if (desired->red_bits != -1) {
  7044. color_diff += (desired->red_bits - current->red_bits) * (desired->red_bits - current->red_bits);
  7045. }
  7046. if (desired->green_bits != -1) {
  7047. color_diff += (desired->green_bits - current->green_bits) * (desired->green_bits - current->green_bits);
  7048. }
  7049. if (desired->blue_bits != -1) {
  7050. color_diff += (desired->blue_bits - current->blue_bits) * (desired->blue_bits - current->blue_bits);
  7051. }
  7052. /* Calculate non-color channel size difference value */
  7053. extra_diff = 0;
  7054. if (desired->alpha_bits != -1) {
  7055. extra_diff += (desired->alpha_bits - current->alpha_bits) * (desired->alpha_bits - current->alpha_bits);
  7056. }
  7057. if (desired->depth_bits != -1) {
  7058. extra_diff += (desired->depth_bits - current->depth_bits) * (desired->depth_bits - current->depth_bits);
  7059. }
  7060. if (desired->stencil_bits != -1) {
  7061. extra_diff += (desired->stencil_bits - current->stencil_bits) * (desired->stencil_bits - current->stencil_bits);
  7062. }
  7063. if (desired->samples != -1) {
  7064. extra_diff += (desired->samples - current->samples) * (desired->samples - current->samples);
  7065. }
  7066. /* Figure out if the current one is better than the best one found so far
  7067. Least number of missing buffers is the most important heuristic,
  7068. then color buffer size match and lastly size match for other buffers
  7069. */
  7070. if (missing < least_missing) {
  7071. closest = current;
  7072. } else if (missing == least_missing) {
  7073. if ((color_diff < least_color_diff) ||
  7074. (color_diff == least_color_diff && extra_diff < least_extra_diff))
  7075. {
  7076. closest = current;
  7077. }
  7078. }
  7079. if (current == closest) {
  7080. least_missing = missing;
  7081. least_color_diff = color_diff;
  7082. least_extra_diff = extra_diff;
  7083. }
  7084. }
  7085. return closest;
  7086. }
  7087. #endif
  7088. // ██ ██ ██ ███ ██ ██████ ██████ ██ ██ ███████
  7089. // ██ ██ ██ ████ ██ ██ ██ ██ ██ ██ ██ ██
  7090. // ██ █ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ █ ██ ███████
  7091. // ██ ███ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ███ ██ ██
  7092. // ███ ███ ██ ██ ████ ██████ ██████ ███ ███ ███████
  7093. //
  7094. // >>windows
  7095. #if defined(_SAPP_WIN32)
  7096. _SOKOL_PRIVATE bool _sapp_win32_utf8_to_wide(const char* src, wchar_t* dst, int dst_num_bytes) {
  7097. SOKOL_ASSERT(src && dst && (dst_num_bytes > 1));
  7098. _sapp_clear(dst, (size_t)dst_num_bytes);
  7099. const int dst_chars = dst_num_bytes / (int)sizeof(wchar_t);
  7100. const int dst_needed = MultiByteToWideChar(CP_UTF8, 0, src, -1, 0, 0);
  7101. if ((dst_needed > 0) && (dst_needed < dst_chars)) {
  7102. MultiByteToWideChar(CP_UTF8, 0, src, -1, dst, dst_chars);
  7103. return true;
  7104. } else {
  7105. // input string doesn't fit into destination buffer
  7106. return false;
  7107. }
  7108. }
  7109. _SOKOL_PRIVATE void _sapp_win32_app_event(sapp_event_type type) {
  7110. if (_sapp_events_enabled()) {
  7111. _sapp_init_event(type);
  7112. _sapp_call_event(&_sapp.event);
  7113. }
  7114. }
  7115. _SOKOL_PRIVATE void _sapp_win32_init_keytable(void) {
  7116. /* same as GLFW */
  7117. _sapp.keycodes[0x00B] = SAPP_KEYCODE_0;
  7118. _sapp.keycodes[0x002] = SAPP_KEYCODE_1;
  7119. _sapp.keycodes[0x003] = SAPP_KEYCODE_2;
  7120. _sapp.keycodes[0x004] = SAPP_KEYCODE_3;
  7121. _sapp.keycodes[0x005] = SAPP_KEYCODE_4;
  7122. _sapp.keycodes[0x006] = SAPP_KEYCODE_5;
  7123. _sapp.keycodes[0x007] = SAPP_KEYCODE_6;
  7124. _sapp.keycodes[0x008] = SAPP_KEYCODE_7;
  7125. _sapp.keycodes[0x009] = SAPP_KEYCODE_8;
  7126. _sapp.keycodes[0x00A] = SAPP_KEYCODE_9;
  7127. _sapp.keycodes[0x01E] = SAPP_KEYCODE_A;
  7128. _sapp.keycodes[0x030] = SAPP_KEYCODE_B;
  7129. _sapp.keycodes[0x02E] = SAPP_KEYCODE_C;
  7130. _sapp.keycodes[0x020] = SAPP_KEYCODE_D;
  7131. _sapp.keycodes[0x012] = SAPP_KEYCODE_E;
  7132. _sapp.keycodes[0x021] = SAPP_KEYCODE_F;
  7133. _sapp.keycodes[0x022] = SAPP_KEYCODE_G;
  7134. _sapp.keycodes[0x023] = SAPP_KEYCODE_H;
  7135. _sapp.keycodes[0x017] = SAPP_KEYCODE_I;
  7136. _sapp.keycodes[0x024] = SAPP_KEYCODE_J;
  7137. _sapp.keycodes[0x025] = SAPP_KEYCODE_K;
  7138. _sapp.keycodes[0x026] = SAPP_KEYCODE_L;
  7139. _sapp.keycodes[0x032] = SAPP_KEYCODE_M;
  7140. _sapp.keycodes[0x031] = SAPP_KEYCODE_N;
  7141. _sapp.keycodes[0x018] = SAPP_KEYCODE_O;
  7142. _sapp.keycodes[0x019] = SAPP_KEYCODE_P;
  7143. _sapp.keycodes[0x010] = SAPP_KEYCODE_Q;
  7144. _sapp.keycodes[0x013] = SAPP_KEYCODE_R;
  7145. _sapp.keycodes[0x01F] = SAPP_KEYCODE_S;
  7146. _sapp.keycodes[0x014] = SAPP_KEYCODE_T;
  7147. _sapp.keycodes[0x016] = SAPP_KEYCODE_U;
  7148. _sapp.keycodes[0x02F] = SAPP_KEYCODE_V;
  7149. _sapp.keycodes[0x011] = SAPP_KEYCODE_W;
  7150. _sapp.keycodes[0x02D] = SAPP_KEYCODE_X;
  7151. _sapp.keycodes[0x015] = SAPP_KEYCODE_Y;
  7152. _sapp.keycodes[0x02C] = SAPP_KEYCODE_Z;
  7153. _sapp.keycodes[0x028] = SAPP_KEYCODE_APOSTROPHE;
  7154. _sapp.keycodes[0x02B] = SAPP_KEYCODE_BACKSLASH;
  7155. _sapp.keycodes[0x033] = SAPP_KEYCODE_COMMA;
  7156. _sapp.keycodes[0x00D] = SAPP_KEYCODE_EQUAL;
  7157. _sapp.keycodes[0x029] = SAPP_KEYCODE_GRAVE_ACCENT;
  7158. _sapp.keycodes[0x01A] = SAPP_KEYCODE_LEFT_BRACKET;
  7159. _sapp.keycodes[0x00C] = SAPP_KEYCODE_MINUS;
  7160. _sapp.keycodes[0x034] = SAPP_KEYCODE_PERIOD;
  7161. _sapp.keycodes[0x01B] = SAPP_KEYCODE_RIGHT_BRACKET;
  7162. _sapp.keycodes[0x027] = SAPP_KEYCODE_SEMICOLON;
  7163. _sapp.keycodes[0x035] = SAPP_KEYCODE_SLASH;
  7164. _sapp.keycodes[0x056] = SAPP_KEYCODE_WORLD_2;
  7165. _sapp.keycodes[0x00E] = SAPP_KEYCODE_BACKSPACE;
  7166. _sapp.keycodes[0x153] = SAPP_KEYCODE_DELETE;
  7167. _sapp.keycodes[0x14F] = SAPP_KEYCODE_END;
  7168. _sapp.keycodes[0x01C] = SAPP_KEYCODE_ENTER;
  7169. _sapp.keycodes[0x001] = SAPP_KEYCODE_ESCAPE;
  7170. _sapp.keycodes[0x147] = SAPP_KEYCODE_HOME;
  7171. _sapp.keycodes[0x152] = SAPP_KEYCODE_INSERT;
  7172. _sapp.keycodes[0x15D] = SAPP_KEYCODE_MENU;
  7173. _sapp.keycodes[0x151] = SAPP_KEYCODE_PAGE_DOWN;
  7174. _sapp.keycodes[0x149] = SAPP_KEYCODE_PAGE_UP;
  7175. _sapp.keycodes[0x045] = SAPP_KEYCODE_PAUSE;
  7176. _sapp.keycodes[0x146] = SAPP_KEYCODE_PAUSE;
  7177. _sapp.keycodes[0x039] = SAPP_KEYCODE_SPACE;
  7178. _sapp.keycodes[0x00F] = SAPP_KEYCODE_TAB;
  7179. _sapp.keycodes[0x03A] = SAPP_KEYCODE_CAPS_LOCK;
  7180. _sapp.keycodes[0x145] = SAPP_KEYCODE_NUM_LOCK;
  7181. _sapp.keycodes[0x046] = SAPP_KEYCODE_SCROLL_LOCK;
  7182. _sapp.keycodes[0x03B] = SAPP_KEYCODE_F1;
  7183. _sapp.keycodes[0x03C] = SAPP_KEYCODE_F2;
  7184. _sapp.keycodes[0x03D] = SAPP_KEYCODE_F3;
  7185. _sapp.keycodes[0x03E] = SAPP_KEYCODE_F4;
  7186. _sapp.keycodes[0x03F] = SAPP_KEYCODE_F5;
  7187. _sapp.keycodes[0x040] = SAPP_KEYCODE_F6;
  7188. _sapp.keycodes[0x041] = SAPP_KEYCODE_F7;
  7189. _sapp.keycodes[0x042] = SAPP_KEYCODE_F8;
  7190. _sapp.keycodes[0x043] = SAPP_KEYCODE_F9;
  7191. _sapp.keycodes[0x044] = SAPP_KEYCODE_F10;
  7192. _sapp.keycodes[0x057] = SAPP_KEYCODE_F11;
  7193. _sapp.keycodes[0x058] = SAPP_KEYCODE_F12;
  7194. _sapp.keycodes[0x064] = SAPP_KEYCODE_F13;
  7195. _sapp.keycodes[0x065] = SAPP_KEYCODE_F14;
  7196. _sapp.keycodes[0x066] = SAPP_KEYCODE_F15;
  7197. _sapp.keycodes[0x067] = SAPP_KEYCODE_F16;
  7198. _sapp.keycodes[0x068] = SAPP_KEYCODE_F17;
  7199. _sapp.keycodes[0x069] = SAPP_KEYCODE_F18;
  7200. _sapp.keycodes[0x06A] = SAPP_KEYCODE_F19;
  7201. _sapp.keycodes[0x06B] = SAPP_KEYCODE_F20;
  7202. _sapp.keycodes[0x06C] = SAPP_KEYCODE_F21;
  7203. _sapp.keycodes[0x06D] = SAPP_KEYCODE_F22;
  7204. _sapp.keycodes[0x06E] = SAPP_KEYCODE_F23;
  7205. _sapp.keycodes[0x076] = SAPP_KEYCODE_F24;
  7206. _sapp.keycodes[0x038] = SAPP_KEYCODE_LEFT_ALT;
  7207. _sapp.keycodes[0x01D] = SAPP_KEYCODE_LEFT_CONTROL;
  7208. _sapp.keycodes[0x02A] = SAPP_KEYCODE_LEFT_SHIFT;
  7209. _sapp.keycodes[0x15B] = SAPP_KEYCODE_LEFT_SUPER;
  7210. _sapp.keycodes[0x137] = SAPP_KEYCODE_PRINT_SCREEN;
  7211. _sapp.keycodes[0x138] = SAPP_KEYCODE_RIGHT_ALT;
  7212. _sapp.keycodes[0x11D] = SAPP_KEYCODE_RIGHT_CONTROL;
  7213. _sapp.keycodes[0x036] = SAPP_KEYCODE_RIGHT_SHIFT;
  7214. _sapp.keycodes[0x136] = SAPP_KEYCODE_RIGHT_SHIFT;
  7215. _sapp.keycodes[0x15C] = SAPP_KEYCODE_RIGHT_SUPER;
  7216. _sapp.keycodes[0x150] = SAPP_KEYCODE_DOWN;
  7217. _sapp.keycodes[0x14B] = SAPP_KEYCODE_LEFT;
  7218. _sapp.keycodes[0x14D] = SAPP_KEYCODE_RIGHT;
  7219. _sapp.keycodes[0x148] = SAPP_KEYCODE_UP;
  7220. _sapp.keycodes[0x052] = SAPP_KEYCODE_KP_0;
  7221. _sapp.keycodes[0x04F] = SAPP_KEYCODE_KP_1;
  7222. _sapp.keycodes[0x050] = SAPP_KEYCODE_KP_2;
  7223. _sapp.keycodes[0x051] = SAPP_KEYCODE_KP_3;
  7224. _sapp.keycodes[0x04B] = SAPP_KEYCODE_KP_4;
  7225. _sapp.keycodes[0x04C] = SAPP_KEYCODE_KP_5;
  7226. _sapp.keycodes[0x04D] = SAPP_KEYCODE_KP_6;
  7227. _sapp.keycodes[0x047] = SAPP_KEYCODE_KP_7;
  7228. _sapp.keycodes[0x048] = SAPP_KEYCODE_KP_8;
  7229. _sapp.keycodes[0x049] = SAPP_KEYCODE_KP_9;
  7230. _sapp.keycodes[0x04E] = SAPP_KEYCODE_KP_ADD;
  7231. _sapp.keycodes[0x053] = SAPP_KEYCODE_KP_DECIMAL;
  7232. _sapp.keycodes[0x135] = SAPP_KEYCODE_KP_DIVIDE;
  7233. _sapp.keycodes[0x11C] = SAPP_KEYCODE_KP_ENTER;
  7234. _sapp.keycodes[0x037] = SAPP_KEYCODE_KP_MULTIPLY;
  7235. _sapp.keycodes[0x04A] = SAPP_KEYCODE_KP_SUBTRACT;
  7236. }
  7237. #endif // _SAPP_WIN32
  7238. #if defined(_SAPP_WIN32)
  7239. #if defined(SOKOL_D3D11)
  7240. #if defined(__cplusplus)
  7241. #define _sapp_d3d11_Release(self) (self)->Release()
  7242. #define _sapp_win32_refiid(iid) iid
  7243. #else
  7244. #define _sapp_d3d11_Release(self) (self)->lpVtbl->Release(self)
  7245. #define _sapp_win32_refiid(iid) &iid
  7246. #endif
  7247. #define _SAPP_SAFE_RELEASE(obj) if (obj) { _sapp_d3d11_Release(obj); obj=0; }
  7248. static const IID _sapp_IID_ID3D11Texture2D = { 0x6f15aaf2,0xd208,0x4e89, {0x9a,0xb4,0x48,0x95,0x35,0xd3,0x4f,0x9c} };
  7249. static const IID _sapp_IID_IDXGIDevice1 = { 0x77db970f,0x6276,0x48ba, {0xba,0x28,0x07,0x01,0x43,0xb4,0x39,0x2c} };
  7250. static const IID _sapp_IID_IDXGIFactory = { 0x7b7166ec,0x21c7,0x44ae, {0xb2,0x1a,0xc9,0xae,0x32,0x1a,0xe3,0x69} };
  7251. static inline HRESULT _sapp_dxgi_GetBuffer(IDXGISwapChain* self, UINT Buffer, REFIID riid, void** ppSurface) {
  7252. #if defined(__cplusplus)
  7253. return self->GetBuffer(Buffer, riid, ppSurface);
  7254. #else
  7255. return self->lpVtbl->GetBuffer(self, Buffer, riid, ppSurface);
  7256. #endif
  7257. }
  7258. static inline HRESULT _sapp_d3d11_QueryInterface(ID3D11Device* self, REFIID riid, void** ppvObject) {
  7259. #if defined(__cplusplus)
  7260. return self->QueryInterface(riid, ppvObject);
  7261. #else
  7262. return self->lpVtbl->QueryInterface(self, riid, ppvObject);
  7263. #endif
  7264. }
  7265. static inline HRESULT _sapp_d3d11_CreateRenderTargetView(ID3D11Device* self, ID3D11Resource *pResource, const D3D11_RENDER_TARGET_VIEW_DESC* pDesc, ID3D11RenderTargetView** ppRTView) {
  7266. #if defined(__cplusplus)
  7267. return self->CreateRenderTargetView(pResource, pDesc, ppRTView);
  7268. #else
  7269. return self->lpVtbl->CreateRenderTargetView(self, pResource, pDesc, ppRTView);
  7270. #endif
  7271. }
  7272. static inline HRESULT _sapp_d3d11_CreateTexture2D(ID3D11Device* self, const D3D11_TEXTURE2D_DESC* pDesc, const D3D11_SUBRESOURCE_DATA* pInitialData, ID3D11Texture2D** ppTexture2D) {
  7273. #if defined(__cplusplus)
  7274. return self->CreateTexture2D(pDesc, pInitialData, ppTexture2D);
  7275. #else
  7276. return self->lpVtbl->CreateTexture2D(self, pDesc, pInitialData, ppTexture2D);
  7277. #endif
  7278. }
  7279. static inline HRESULT _sapp_d3d11_CreateDepthStencilView(ID3D11Device* self, ID3D11Resource* pResource, const D3D11_DEPTH_STENCIL_VIEW_DESC* pDesc, ID3D11DepthStencilView** ppDepthStencilView) {
  7280. #if defined(__cplusplus)
  7281. return self->CreateDepthStencilView(pResource, pDesc, ppDepthStencilView);
  7282. #else
  7283. return self->lpVtbl->CreateDepthStencilView(self, pResource, pDesc, ppDepthStencilView);
  7284. #endif
  7285. }
  7286. static inline HRESULT _sapp_dxgi_ResizeBuffers(IDXGISwapChain* self, UINT BufferCount, UINT Width, UINT Height, DXGI_FORMAT NewFormat, UINT SwapChainFlags) {
  7287. #if defined(__cplusplus)
  7288. return self->ResizeBuffers(BufferCount, Width, Height, NewFormat, SwapChainFlags);
  7289. #else
  7290. return self->lpVtbl->ResizeBuffers(self, BufferCount, Width, Height, NewFormat, SwapChainFlags);
  7291. #endif
  7292. }
  7293. static inline HRESULT _sapp_dxgi_Present(IDXGISwapChain* self, UINT SyncInterval, UINT Flags) {
  7294. #if defined(__cplusplus)
  7295. return self->Present(SyncInterval, Flags);
  7296. #else
  7297. return self->lpVtbl->Present(self, SyncInterval, Flags);
  7298. #endif
  7299. }
  7300. static inline HRESULT _sapp_dxgi_GetFrameStatistics(IDXGISwapChain* self, DXGI_FRAME_STATISTICS* pStats) {
  7301. #if defined(__cplusplus)
  7302. return self->GetFrameStatistics(pStats);
  7303. #else
  7304. return self->lpVtbl->GetFrameStatistics(self, pStats);
  7305. #endif
  7306. }
  7307. static inline HRESULT _sapp_dxgi_SetMaximumFrameLatency(IDXGIDevice1* self, UINT MaxLatency) {
  7308. #if defined(__cplusplus)
  7309. return self->SetMaximumFrameLatency(MaxLatency);
  7310. #else
  7311. return self->lpVtbl->SetMaximumFrameLatency(self, MaxLatency);
  7312. #endif
  7313. }
  7314. static inline HRESULT _sapp_dxgi_GetAdapter(IDXGIDevice1* self, IDXGIAdapter** pAdapter) {
  7315. #if defined(__cplusplus)
  7316. return self->GetAdapter(pAdapter);
  7317. #else
  7318. return self->lpVtbl->GetAdapter(self, pAdapter);
  7319. #endif
  7320. }
  7321. static inline HRESULT _sapp_dxgi_GetParent(IDXGIObject* self, REFIID riid, void** ppParent) {
  7322. #if defined(__cplusplus)
  7323. return self->GetParent(riid, ppParent);
  7324. #else
  7325. return self->lpVtbl->GetParent(self, riid, ppParent);
  7326. #endif
  7327. }
  7328. static inline HRESULT _sapp_dxgi_MakeWindowAssociation(IDXGIFactory* self, HWND WindowHandle, UINT Flags) {
  7329. #if defined(__cplusplus)
  7330. return self->MakeWindowAssociation(WindowHandle, Flags);
  7331. #else
  7332. return self->lpVtbl->MakeWindowAssociation(self, WindowHandle, Flags);
  7333. #endif
  7334. }
  7335. _SOKOL_PRIVATE void _sapp_d3d11_create_device_and_swapchain(void) {
  7336. DXGI_SWAP_CHAIN_DESC* sc_desc = &_sapp.d3d11.swap_chain_desc;
  7337. sc_desc->BufferDesc.Width = (UINT)_sapp.framebuffer_width;
  7338. sc_desc->BufferDesc.Height = (UINT)_sapp.framebuffer_height;
  7339. sc_desc->BufferDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
  7340. sc_desc->BufferDesc.RefreshRate.Numerator = 60;
  7341. sc_desc->BufferDesc.RefreshRate.Denominator = 1;
  7342. sc_desc->OutputWindow = _sapp.win32.hwnd;
  7343. sc_desc->Windowed = true;
  7344. if (_sapp.win32.is_win10_or_greater) {
  7345. sc_desc->BufferCount = 2;
  7346. sc_desc->SwapEffect = (DXGI_SWAP_EFFECT) _SAPP_DXGI_SWAP_EFFECT_FLIP_DISCARD;
  7347. _sapp.d3d11.use_dxgi_frame_stats = true;
  7348. } else {
  7349. sc_desc->BufferCount = 1;
  7350. sc_desc->SwapEffect = DXGI_SWAP_EFFECT_DISCARD;
  7351. _sapp.d3d11.use_dxgi_frame_stats = false;
  7352. }
  7353. sc_desc->SampleDesc.Count = 1;
  7354. sc_desc->SampleDesc.Quality = 0;
  7355. sc_desc->BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
  7356. UINT create_flags = D3D11_CREATE_DEVICE_SINGLETHREADED | D3D11_CREATE_DEVICE_BGRA_SUPPORT;
  7357. #if defined(SOKOL_DEBUG)
  7358. create_flags |= D3D11_CREATE_DEVICE_DEBUG;
  7359. #endif
  7360. D3D_FEATURE_LEVEL requested_feature_levels[] = { D3D_FEATURE_LEVEL_11_1, D3D_FEATURE_LEVEL_11_0 };
  7361. D3D_FEATURE_LEVEL result_feature_level;
  7362. HRESULT hr = D3D11CreateDeviceAndSwapChain(
  7363. NULL, /* pAdapter (use default) */
  7364. D3D_DRIVER_TYPE_HARDWARE, /* DriverType */
  7365. NULL, /* Software */
  7366. create_flags, /* Flags */
  7367. requested_feature_levels, /* pFeatureLevels */
  7368. 2, /* FeatureLevels */
  7369. D3D11_SDK_VERSION, /* SDKVersion */
  7370. sc_desc, /* pSwapChainDesc */
  7371. &_sapp.d3d11.swap_chain, /* ppSwapChain */
  7372. &_sapp.d3d11.device, /* ppDevice */
  7373. &result_feature_level, /* pFeatureLevel */
  7374. &_sapp.d3d11.device_context); /* ppImmediateContext */
  7375. _SOKOL_UNUSED(hr);
  7376. #if defined(SOKOL_DEBUG)
  7377. if (!SUCCEEDED(hr)) {
  7378. // if initialization with D3D11_CREATE_DEVICE_DEBUG fails, this could be because the
  7379. // 'D3D11 debug layer' stopped working, indicated by the error message:
  7380. // ===
  7381. // D3D11CreateDevice: Flags (0x2) were specified which require the D3D11 SDK Layers for Windows 10, but they are not present on the system.
  7382. // These flags must be removed, or the Windows 10 SDK must be installed.
  7383. // Flags include: D3D11_CREATE_DEVICE_DEBUG
  7384. // ===
  7385. //
  7386. // ...just retry with the DEBUG flag switched off
  7387. _SAPP_ERROR(WIN32_D3D11_CREATE_DEVICE_AND_SWAPCHAIN_WITH_DEBUG_FAILED);
  7388. create_flags &= ~(UINT)D3D11_CREATE_DEVICE_DEBUG;
  7389. hr = D3D11CreateDeviceAndSwapChain(
  7390. NULL, /* pAdapter (use default) */
  7391. D3D_DRIVER_TYPE_HARDWARE, /* DriverType */
  7392. NULL, /* Software */
  7393. create_flags, /* Flags */
  7394. requested_feature_levels, /* pFeatureLevels */
  7395. 2, /* FeatureLevels */
  7396. D3D11_SDK_VERSION, /* SDKVersion */
  7397. sc_desc, /* pSwapChainDesc */
  7398. &_sapp.d3d11.swap_chain, /* ppSwapChain */
  7399. &_sapp.d3d11.device, /* ppDevice */
  7400. &result_feature_level, /* pFeatureLevel */
  7401. &_sapp.d3d11.device_context); /* ppImmediateContext */
  7402. }
  7403. #endif
  7404. SOKOL_ASSERT(SUCCEEDED(hr) && _sapp.d3d11.swap_chain && _sapp.d3d11.device && _sapp.d3d11.device_context);
  7405. // minimize frame latency, disable Alt-Enter
  7406. hr = _sapp_d3d11_QueryInterface(_sapp.d3d11.device, _sapp_win32_refiid(_sapp_IID_IDXGIDevice1), (void**)&_sapp.d3d11.dxgi_device);
  7407. if (SUCCEEDED(hr) && _sapp.d3d11.dxgi_device) {
  7408. _sapp_dxgi_SetMaximumFrameLatency(_sapp.d3d11.dxgi_device, 1);
  7409. IDXGIAdapter* dxgi_adapter = 0;
  7410. hr = _sapp_dxgi_GetAdapter(_sapp.d3d11.dxgi_device, &dxgi_adapter);
  7411. if (SUCCEEDED(hr) && dxgi_adapter) {
  7412. IDXGIFactory* dxgi_factory = 0;
  7413. hr = _sapp_dxgi_GetParent((IDXGIObject*)dxgi_adapter, _sapp_win32_refiid(_sapp_IID_IDXGIFactory), (void**)&dxgi_factory);
  7414. if (SUCCEEDED(hr)) {
  7415. _sapp_dxgi_MakeWindowAssociation(dxgi_factory, _sapp.win32.hwnd, DXGI_MWA_NO_ALT_ENTER|DXGI_MWA_NO_PRINT_SCREEN);
  7416. _SAPP_SAFE_RELEASE(dxgi_factory);
  7417. } else {
  7418. _SAPP_ERROR(WIN32_D3D11_GET_IDXGIFACTORY_FAILED);
  7419. }
  7420. _SAPP_SAFE_RELEASE(dxgi_adapter);
  7421. } else {
  7422. _SAPP_ERROR(WIN32_D3D11_GET_IDXGIADAPTER_FAILED);
  7423. }
  7424. } else {
  7425. _SAPP_PANIC(WIN32_D3D11_QUERY_INTERFACE_IDXGIDEVICE1_FAILED);
  7426. }
  7427. }
  7428. _SOKOL_PRIVATE void _sapp_d3d11_destroy_device_and_swapchain(void) {
  7429. _SAPP_SAFE_RELEASE(_sapp.d3d11.swap_chain);
  7430. _SAPP_SAFE_RELEASE(_sapp.d3d11.dxgi_device);
  7431. _SAPP_SAFE_RELEASE(_sapp.d3d11.device_context);
  7432. _SAPP_SAFE_RELEASE(_sapp.d3d11.device);
  7433. }
  7434. _SOKOL_PRIVATE void _sapp_d3d11_create_default_render_target(void) {
  7435. SOKOL_ASSERT(0 == _sapp.d3d11.rt);
  7436. SOKOL_ASSERT(0 == _sapp.d3d11.rtv);
  7437. SOKOL_ASSERT(0 == _sapp.d3d11.msaa_rt);
  7438. SOKOL_ASSERT(0 == _sapp.d3d11.msaa_rtv);
  7439. SOKOL_ASSERT(0 == _sapp.d3d11.ds);
  7440. SOKOL_ASSERT(0 == _sapp.d3d11.dsv);
  7441. HRESULT hr; _SOKOL_UNUSED(hr);
  7442. /* view for the swapchain-created framebuffer */
  7443. hr = _sapp_dxgi_GetBuffer(_sapp.d3d11.swap_chain, 0, _sapp_win32_refiid(_sapp_IID_ID3D11Texture2D), (void**)&_sapp.d3d11.rt);
  7444. SOKOL_ASSERT(SUCCEEDED(hr) && _sapp.d3d11.rt);
  7445. hr = _sapp_d3d11_CreateRenderTargetView(_sapp.d3d11.device, (ID3D11Resource*)_sapp.d3d11.rt, NULL, &_sapp.d3d11.rtv);
  7446. SOKOL_ASSERT(SUCCEEDED(hr) && _sapp.d3d11.rtv);
  7447. /* common desc for MSAA and depth-stencil texture */
  7448. _SAPP_STRUCT(D3D11_TEXTURE2D_DESC, tex_desc);
  7449. tex_desc.Width = (UINT)_sapp.framebuffer_width;
  7450. tex_desc.Height = (UINT)_sapp.framebuffer_height;
  7451. tex_desc.MipLevels = 1;
  7452. tex_desc.ArraySize = 1;
  7453. tex_desc.Usage = D3D11_USAGE_DEFAULT;
  7454. tex_desc.BindFlags = D3D11_BIND_RENDER_TARGET;
  7455. tex_desc.SampleDesc.Count = (UINT) _sapp.sample_count;
  7456. tex_desc.SampleDesc.Quality = (UINT) (_sapp.sample_count > 1 ? D3D11_STANDARD_MULTISAMPLE_PATTERN : 0);
  7457. /* create MSAA texture and view if antialiasing requested */
  7458. if (_sapp.sample_count > 1) {
  7459. tex_desc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
  7460. hr = _sapp_d3d11_CreateTexture2D(_sapp.d3d11.device, &tex_desc, NULL, &_sapp.d3d11.msaa_rt);
  7461. SOKOL_ASSERT(SUCCEEDED(hr) && _sapp.d3d11.msaa_rt);
  7462. hr = _sapp_d3d11_CreateRenderTargetView(_sapp.d3d11.device, (ID3D11Resource*)_sapp.d3d11.msaa_rt, NULL, &_sapp.d3d11.msaa_rtv);
  7463. SOKOL_ASSERT(SUCCEEDED(hr) && _sapp.d3d11.msaa_rtv);
  7464. }
  7465. /* texture and view for the depth-stencil-surface */
  7466. tex_desc.Format = DXGI_FORMAT_D24_UNORM_S8_UINT;
  7467. tex_desc.BindFlags = D3D11_BIND_DEPTH_STENCIL;
  7468. hr = _sapp_d3d11_CreateTexture2D(_sapp.d3d11.device, &tex_desc, NULL, &_sapp.d3d11.ds);
  7469. SOKOL_ASSERT(SUCCEEDED(hr) && _sapp.d3d11.ds);
  7470. hr = _sapp_d3d11_CreateDepthStencilView(_sapp.d3d11.device, (ID3D11Resource*)_sapp.d3d11.ds, NULL, &_sapp.d3d11.dsv);
  7471. SOKOL_ASSERT(SUCCEEDED(hr) && _sapp.d3d11.dsv);
  7472. }
  7473. _SOKOL_PRIVATE void _sapp_d3d11_destroy_default_render_target(void) {
  7474. _SAPP_SAFE_RELEASE(_sapp.d3d11.rt);
  7475. _SAPP_SAFE_RELEASE(_sapp.d3d11.rtv);
  7476. _SAPP_SAFE_RELEASE(_sapp.d3d11.msaa_rt);
  7477. _SAPP_SAFE_RELEASE(_sapp.d3d11.msaa_rtv);
  7478. _SAPP_SAFE_RELEASE(_sapp.d3d11.ds);
  7479. _SAPP_SAFE_RELEASE(_sapp.d3d11.dsv);
  7480. }
  7481. _SOKOL_PRIVATE void _sapp_d3d11_resize_default_render_target(void) {
  7482. if (_sapp.d3d11.swap_chain) {
  7483. _sapp_d3d11_destroy_default_render_target();
  7484. _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);
  7485. _sapp_d3d11_create_default_render_target();
  7486. }
  7487. }
  7488. _SOKOL_PRIVATE void _sapp_d3d11_present(bool do_not_wait) {
  7489. UINT flags = 0;
  7490. if (_sapp.win32.is_win10_or_greater && do_not_wait) {
  7491. /* this hack/workaround somewhat improves window-movement and -sizing
  7492. responsiveness when rendering is controlled via WM_TIMER during window
  7493. move and resize on NVIDIA cards on Win10 with recent drivers.
  7494. */
  7495. flags = DXGI_PRESENT_DO_NOT_WAIT;
  7496. }
  7497. _sapp_dxgi_Present(_sapp.d3d11.swap_chain, (UINT)_sapp.swap_interval, flags);
  7498. }
  7499. #endif /* SOKOL_D3D11 */
  7500. #if defined(SOKOL_GLCORE)
  7501. _SOKOL_PRIVATE void _sapp_wgl_init(void) {
  7502. _sapp.wgl.opengl32 = LoadLibraryA("opengl32.dll");
  7503. if (!_sapp.wgl.opengl32) {
  7504. _SAPP_PANIC(WIN32_LOAD_OPENGL32_DLL_FAILED);
  7505. }
  7506. SOKOL_ASSERT(_sapp.wgl.opengl32);
  7507. _sapp.wgl.CreateContext = (PFN_wglCreateContext)(void*) GetProcAddress(_sapp.wgl.opengl32, "wglCreateContext");
  7508. SOKOL_ASSERT(_sapp.wgl.CreateContext);
  7509. _sapp.wgl.DeleteContext = (PFN_wglDeleteContext)(void*) GetProcAddress(_sapp.wgl.opengl32, "wglDeleteContext");
  7510. SOKOL_ASSERT(_sapp.wgl.DeleteContext);
  7511. _sapp.wgl.GetProcAddress = (PFN_wglGetProcAddress)(void*) GetProcAddress(_sapp.wgl.opengl32, "wglGetProcAddress");
  7512. SOKOL_ASSERT(_sapp.wgl.GetProcAddress);
  7513. _sapp.wgl.GetCurrentDC = (PFN_wglGetCurrentDC)(void*) GetProcAddress(_sapp.wgl.opengl32, "wglGetCurrentDC");
  7514. SOKOL_ASSERT(_sapp.wgl.GetCurrentDC);
  7515. _sapp.wgl.MakeCurrent = (PFN_wglMakeCurrent)(void*) GetProcAddress(_sapp.wgl.opengl32, "wglMakeCurrent");
  7516. SOKOL_ASSERT(_sapp.wgl.MakeCurrent);
  7517. _sapp.wgl.GetIntegerv = (void(WINAPI*)(uint32_t, int32_t*)) GetProcAddress(_sapp.wgl.opengl32, "glGetIntegerv");
  7518. SOKOL_ASSERT(_sapp.wgl.GetIntegerv);
  7519. _sapp.wgl.msg_hwnd = CreateWindowExW(WS_EX_OVERLAPPEDWINDOW,
  7520. L"SOKOLAPP",
  7521. L"sokol-app message window",
  7522. WS_CLIPSIBLINGS|WS_CLIPCHILDREN,
  7523. 0, 0, 1, 1,
  7524. NULL, NULL,
  7525. GetModuleHandleW(NULL),
  7526. NULL);
  7527. if (!_sapp.wgl.msg_hwnd) {
  7528. _SAPP_PANIC(WIN32_CREATE_HELPER_WINDOW_FAILED);
  7529. }
  7530. SOKOL_ASSERT(_sapp.wgl.msg_hwnd);
  7531. ShowWindow(_sapp.wgl.msg_hwnd, SW_HIDE);
  7532. MSG msg;
  7533. while (PeekMessageW(&msg, _sapp.wgl.msg_hwnd, 0, 0, PM_REMOVE)) {
  7534. TranslateMessage(&msg);
  7535. DispatchMessageW(&msg);
  7536. }
  7537. _sapp.wgl.msg_dc = GetDC(_sapp.wgl.msg_hwnd);
  7538. if (!_sapp.wgl.msg_dc) {
  7539. _SAPP_PANIC(WIN32_HELPER_WINDOW_GETDC_FAILED);
  7540. }
  7541. }
  7542. _SOKOL_PRIVATE void _sapp_wgl_shutdown(void) {
  7543. SOKOL_ASSERT(_sapp.wgl.opengl32 && _sapp.wgl.msg_hwnd);
  7544. DestroyWindow(_sapp.wgl.msg_hwnd); _sapp.wgl.msg_hwnd = 0;
  7545. FreeLibrary(_sapp.wgl.opengl32); _sapp.wgl.opengl32 = 0;
  7546. }
  7547. _SOKOL_PRIVATE bool _sapp_wgl_has_ext(const char* ext, const char* extensions) {
  7548. SOKOL_ASSERT(ext && extensions);
  7549. const char* start = extensions;
  7550. while (true) {
  7551. const char* where = strstr(start, ext);
  7552. if (!where) {
  7553. return false;
  7554. }
  7555. const char* terminator = where + strlen(ext);
  7556. if ((where == start) || (*(where - 1) == ' ')) {
  7557. if (*terminator == ' ' || *terminator == '\0') {
  7558. break;
  7559. }
  7560. }
  7561. start = terminator;
  7562. }
  7563. return true;
  7564. }
  7565. _SOKOL_PRIVATE bool _sapp_wgl_ext_supported(const char* ext) {
  7566. SOKOL_ASSERT(ext);
  7567. if (_sapp.wgl.GetExtensionsStringEXT) {
  7568. const char* extensions = _sapp.wgl.GetExtensionsStringEXT();
  7569. if (extensions) {
  7570. if (_sapp_wgl_has_ext(ext, extensions)) {
  7571. return true;
  7572. }
  7573. }
  7574. }
  7575. if (_sapp.wgl.GetExtensionsStringARB) {
  7576. const char* extensions = _sapp.wgl.GetExtensionsStringARB(_sapp.wgl.GetCurrentDC());
  7577. if (extensions) {
  7578. if (_sapp_wgl_has_ext(ext, extensions)) {
  7579. return true;
  7580. }
  7581. }
  7582. }
  7583. return false;
  7584. }
  7585. _SOKOL_PRIVATE void _sapp_wgl_load_extensions(void) {
  7586. SOKOL_ASSERT(_sapp.wgl.msg_dc);
  7587. _SAPP_STRUCT(PIXELFORMATDESCRIPTOR, pfd);
  7588. pfd.nSize = sizeof(pfd);
  7589. pfd.nVersion = 1;
  7590. pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER;
  7591. pfd.iPixelType = PFD_TYPE_RGBA;
  7592. pfd.cColorBits = 24;
  7593. if (!SetPixelFormat(_sapp.wgl.msg_dc, ChoosePixelFormat(_sapp.wgl.msg_dc, &pfd), &pfd)) {
  7594. _SAPP_PANIC(WIN32_DUMMY_CONTEXT_SET_PIXELFORMAT_FAILED);
  7595. }
  7596. HGLRC rc = _sapp.wgl.CreateContext(_sapp.wgl.msg_dc);
  7597. if (!rc) {
  7598. _SAPP_PANIC(WIN32_CREATE_DUMMY_CONTEXT_FAILED);
  7599. }
  7600. if (!_sapp.wgl.MakeCurrent(_sapp.wgl.msg_dc, rc)) {
  7601. _SAPP_PANIC(WIN32_DUMMY_CONTEXT_MAKE_CURRENT_FAILED);
  7602. }
  7603. _sapp.wgl.GetExtensionsStringEXT = (PFNWGLGETEXTENSIONSSTRINGEXTPROC)(void*) _sapp.wgl.GetProcAddress("wglGetExtensionsStringEXT");
  7604. _sapp.wgl.GetExtensionsStringARB = (PFNWGLGETEXTENSIONSSTRINGARBPROC)(void*) _sapp.wgl.GetProcAddress("wglGetExtensionsStringARB");
  7605. _sapp.wgl.CreateContextAttribsARB = (PFNWGLCREATECONTEXTATTRIBSARBPROC)(void*) _sapp.wgl.GetProcAddress("wglCreateContextAttribsARB");
  7606. _sapp.wgl.SwapIntervalEXT = (PFNWGLSWAPINTERVALEXTPROC)(void*) _sapp.wgl.GetProcAddress("wglSwapIntervalEXT");
  7607. _sapp.wgl.GetPixelFormatAttribivARB = (PFNWGLGETPIXELFORMATATTRIBIVARBPROC)(void*) _sapp.wgl.GetProcAddress("wglGetPixelFormatAttribivARB");
  7608. _sapp.wgl.arb_multisample = _sapp_wgl_ext_supported("WGL_ARB_multisample");
  7609. _sapp.wgl.arb_create_context = _sapp_wgl_ext_supported("WGL_ARB_create_context");
  7610. _sapp.wgl.arb_create_context_profile = _sapp_wgl_ext_supported("WGL_ARB_create_context_profile");
  7611. _sapp.wgl.ext_swap_control = _sapp_wgl_ext_supported("WGL_EXT_swap_control");
  7612. _sapp.wgl.arb_pixel_format = _sapp_wgl_ext_supported("WGL_ARB_pixel_format");
  7613. _sapp.wgl.MakeCurrent(_sapp.wgl.msg_dc, 0);
  7614. _sapp.wgl.DeleteContext(rc);
  7615. }
  7616. _SOKOL_PRIVATE int _sapp_wgl_attrib(int pixel_format, int attrib) {
  7617. SOKOL_ASSERT(_sapp.wgl.arb_pixel_format);
  7618. int value = 0;
  7619. if (!_sapp.wgl.GetPixelFormatAttribivARB(_sapp.win32.dc, pixel_format, 0, 1, &attrib, &value)) {
  7620. _SAPP_PANIC(WIN32_GET_PIXELFORMAT_ATTRIB_FAILED);
  7621. }
  7622. return value;
  7623. }
  7624. _SOKOL_PRIVATE void _sapp_wgl_attribiv(int pixel_format, int num_attribs, const int* attribs, int* results) {
  7625. SOKOL_ASSERT(_sapp.wgl.arb_pixel_format);
  7626. if (!_sapp.wgl.GetPixelFormatAttribivARB(_sapp.win32.dc, pixel_format, 0, num_attribs, attribs, results)) {
  7627. _SAPP_PANIC(WIN32_GET_PIXELFORMAT_ATTRIB_FAILED);
  7628. }
  7629. }
  7630. _SOKOL_PRIVATE int _sapp_wgl_find_pixel_format(void) {
  7631. SOKOL_ASSERT(_sapp.win32.dc);
  7632. SOKOL_ASSERT(_sapp.wgl.arb_pixel_format);
  7633. #define _sapp_wgl_num_query_tags (12)
  7634. const int query_tags[_sapp_wgl_num_query_tags] = {
  7635. WGL_SUPPORT_OPENGL_ARB,
  7636. WGL_DRAW_TO_WINDOW_ARB,
  7637. WGL_PIXEL_TYPE_ARB,
  7638. WGL_ACCELERATION_ARB,
  7639. WGL_DOUBLE_BUFFER_ARB,
  7640. WGL_RED_BITS_ARB,
  7641. WGL_GREEN_BITS_ARB,
  7642. WGL_BLUE_BITS_ARB,
  7643. WGL_ALPHA_BITS_ARB,
  7644. WGL_DEPTH_BITS_ARB,
  7645. WGL_STENCIL_BITS_ARB,
  7646. WGL_SAMPLES_ARB,
  7647. };
  7648. const int result_support_opengl_index = 0;
  7649. const int result_draw_to_window_index = 1;
  7650. const int result_pixel_type_index = 2;
  7651. const int result_acceleration_index = 3;
  7652. const int result_double_buffer_index = 4;
  7653. const int result_red_bits_index = 5;
  7654. const int result_green_bits_index = 6;
  7655. const int result_blue_bits_index = 7;
  7656. const int result_alpha_bits_index = 8;
  7657. const int result_depth_bits_index = 9;
  7658. const int result_stencil_bits_index = 10;
  7659. const int result_samples_index = 11;
  7660. int query_results[_sapp_wgl_num_query_tags] = {0};
  7661. // Drop the last item if multisample extension is not supported.
  7662. // If in future querying with multiple extensions, will have to shuffle index values to have active extensions on the end.
  7663. int query_count = _sapp_wgl_num_query_tags;
  7664. if (!_sapp.wgl.arb_multisample) {
  7665. query_count = _sapp_wgl_num_query_tags - 1;
  7666. }
  7667. int native_count = _sapp_wgl_attrib(1, WGL_NUMBER_PIXEL_FORMATS_ARB);
  7668. _sapp_gl_fbconfig desired;
  7669. _sapp_gl_init_fbconfig(&desired);
  7670. desired.red_bits = 8;
  7671. desired.green_bits = 8;
  7672. desired.blue_bits = 8;
  7673. desired.alpha_bits = 8;
  7674. desired.depth_bits = 24;
  7675. desired.stencil_bits = 8;
  7676. desired.doublebuffer = true;
  7677. desired.samples = (_sapp.sample_count > 1) ? _sapp.sample_count : 0;
  7678. int pixel_format = 0;
  7679. _sapp_gl_fbselect fbselect;
  7680. _sapp_gl_init_fbselect(&fbselect);
  7681. for (int i = 0; i < native_count; i++) {
  7682. const int n = i + 1;
  7683. _sapp_wgl_attribiv(n, query_count, query_tags, query_results);
  7684. if (query_results[result_support_opengl_index] == 0
  7685. || query_results[result_draw_to_window_index] == 0
  7686. || query_results[result_pixel_type_index] != WGL_TYPE_RGBA_ARB
  7687. || query_results[result_acceleration_index] == WGL_NO_ACCELERATION_ARB)
  7688. {
  7689. continue;
  7690. }
  7691. _SAPP_STRUCT(_sapp_gl_fbconfig, u);
  7692. u.red_bits = query_results[result_red_bits_index];
  7693. u.green_bits = query_results[result_green_bits_index];
  7694. u.blue_bits = query_results[result_blue_bits_index];
  7695. u.alpha_bits = query_results[result_alpha_bits_index];
  7696. u.depth_bits = query_results[result_depth_bits_index];
  7697. u.stencil_bits = query_results[result_stencil_bits_index];
  7698. u.doublebuffer = 0 != query_results[result_double_buffer_index];
  7699. u.samples = query_results[result_samples_index]; // NOTE: If arb_multisample is not supported - just takes the default 0
  7700. // Test if this pixel format is better than the previous one
  7701. if (_sapp_gl_select_fbconfig(&fbselect, &desired, &u)) {
  7702. pixel_format = (uintptr_t)n;
  7703. // Early exit if matching as good as possible
  7704. if (fbselect.best_match) {
  7705. break;
  7706. }
  7707. }
  7708. }
  7709. return pixel_format;
  7710. }
  7711. _SOKOL_PRIVATE void _sapp_wgl_create_context(void) {
  7712. int pixel_format = _sapp_wgl_find_pixel_format();
  7713. if (0 == pixel_format) {
  7714. _SAPP_PANIC(WIN32_WGL_FIND_PIXELFORMAT_FAILED);
  7715. }
  7716. PIXELFORMATDESCRIPTOR pfd;
  7717. if (!DescribePixelFormat(_sapp.win32.dc, pixel_format, sizeof(pfd), &pfd)) {
  7718. _SAPP_PANIC(WIN32_WGL_DESCRIBE_PIXELFORMAT_FAILED);
  7719. }
  7720. if (!SetPixelFormat(_sapp.win32.dc, pixel_format, &pfd)) {
  7721. _SAPP_PANIC(WIN32_WGL_SET_PIXELFORMAT_FAILED);
  7722. }
  7723. if (!_sapp.wgl.arb_create_context) {
  7724. _SAPP_PANIC(WIN32_WGL_ARB_CREATE_CONTEXT_REQUIRED);
  7725. }
  7726. if (!_sapp.wgl.arb_create_context_profile) {
  7727. _SAPP_PANIC(WIN32_WGL_ARB_CREATE_CONTEXT_PROFILE_REQUIRED);
  7728. }
  7729. const int attrs[] = {
  7730. WGL_CONTEXT_MAJOR_VERSION_ARB, _sapp.desc.gl.major_version,
  7731. WGL_CONTEXT_MINOR_VERSION_ARB, _sapp.desc.gl.minor_version,
  7732. #if defined(SOKOL_DEBUG)
  7733. WGL_CONTEXT_FLAGS_ARB, WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB | WGL_CONTEXT_DEBUG_BIT_ARB,
  7734. #else
  7735. WGL_CONTEXT_FLAGS_ARB, WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB,
  7736. #endif
  7737. WGL_CONTEXT_PROFILE_MASK_ARB, WGL_CONTEXT_CORE_PROFILE_BIT_ARB,
  7738. 0, 0
  7739. };
  7740. _sapp.wgl.gl_ctx = _sapp.wgl.CreateContextAttribsARB(_sapp.win32.dc, 0, attrs);
  7741. if (!_sapp.wgl.gl_ctx) {
  7742. const DWORD err = GetLastError();
  7743. if (err == (0xc0070000 | ERROR_INVALID_VERSION_ARB)) {
  7744. _SAPP_PANIC(WIN32_WGL_OPENGL_VERSION_NOT_SUPPORTED);
  7745. } else if (err == (0xc0070000 | ERROR_INVALID_PROFILE_ARB)) {
  7746. _SAPP_PANIC(WIN32_WGL_OPENGL_PROFILE_NOT_SUPPORTED);
  7747. } else if (err == (0xc0070000 | ERROR_INCOMPATIBLE_DEVICE_CONTEXTS_ARB)) {
  7748. _SAPP_PANIC(WIN32_WGL_INCOMPATIBLE_DEVICE_CONTEXT);
  7749. } else {
  7750. _SAPP_PANIC(WIN32_WGL_CREATE_CONTEXT_ATTRIBS_FAILED_OTHER);
  7751. }
  7752. }
  7753. _sapp.wgl.MakeCurrent(_sapp.win32.dc, _sapp.wgl.gl_ctx);
  7754. if (_sapp.wgl.ext_swap_control) {
  7755. /* FIXME: DwmIsCompositionEnabled() (see GLFW) */
  7756. _sapp.wgl.SwapIntervalEXT(_sapp.swap_interval);
  7757. }
  7758. const uint32_t gl_framebuffer_binding = 0x8CA6;
  7759. _sapp.wgl.GetIntegerv(gl_framebuffer_binding, (int32_t*)&_sapp.gl.framebuffer);
  7760. }
  7761. _SOKOL_PRIVATE void _sapp_wgl_destroy_context(void) {
  7762. SOKOL_ASSERT(_sapp.wgl.gl_ctx);
  7763. _sapp.wgl.DeleteContext(_sapp.wgl.gl_ctx);
  7764. _sapp.wgl.gl_ctx = 0;
  7765. }
  7766. _SOKOL_PRIVATE void _sapp_wgl_swap_buffers(void) {
  7767. SOKOL_ASSERT(_sapp.win32.dc);
  7768. /* FIXME: DwmIsCompositionEnabled? (see GLFW) */
  7769. SwapBuffers(_sapp.win32.dc);
  7770. }
  7771. #endif /* SOKOL_GLCORE */
  7772. _SOKOL_PRIVATE bool _sapp_win32_wide_to_utf8(const wchar_t* src, char* dst, int dst_num_bytes) {
  7773. SOKOL_ASSERT(src && dst && (dst_num_bytes > 1));
  7774. _sapp_clear(dst, (size_t)dst_num_bytes);
  7775. const int bytes_needed = WideCharToMultiByte(CP_UTF8, 0, src, -1, NULL, 0, NULL, NULL);
  7776. if (bytes_needed <= dst_num_bytes) {
  7777. WideCharToMultiByte(CP_UTF8, 0, src, -1, dst, dst_num_bytes, NULL, NULL);
  7778. return true;
  7779. } else {
  7780. return false;
  7781. }
  7782. }
  7783. /* updates current window and framebuffer size from the window's client rect, returns true if size has changed */
  7784. _SOKOL_PRIVATE bool _sapp_win32_update_dimensions(void) {
  7785. RECT rect;
  7786. if (GetClientRect(_sapp.win32.hwnd, &rect)) {
  7787. float window_width = (float)(rect.right - rect.left) / _sapp.win32.dpi.window_scale;
  7788. float window_height = (float)(rect.bottom - rect.top) / _sapp.win32.dpi.window_scale;
  7789. _sapp.window_width = _sapp_roundf_gzero(window_width);
  7790. _sapp.window_height = _sapp_roundf_gzero(window_height);
  7791. int fb_width = _sapp_roundf_gzero(window_width * _sapp.win32.dpi.content_scale);
  7792. int fb_height = _sapp_roundf_gzero(window_height * _sapp.win32.dpi.content_scale);
  7793. if ((fb_width != _sapp.framebuffer_width) || (fb_height != _sapp.framebuffer_height)) {
  7794. _sapp.framebuffer_width = fb_width;
  7795. _sapp.framebuffer_height = fb_height;
  7796. return true;
  7797. }
  7798. } else {
  7799. _sapp.window_width = _sapp.window_height = 1;
  7800. _sapp.framebuffer_width = _sapp.framebuffer_height = 1;
  7801. }
  7802. return false;
  7803. }
  7804. _SOKOL_PRIVATE void _sapp_win32_set_fullscreen(bool fullscreen, UINT swp_flags) {
  7805. HMONITOR monitor = MonitorFromWindow(_sapp.win32.hwnd, MONITOR_DEFAULTTONEAREST);
  7806. _SAPP_STRUCT(MONITORINFO, minfo);
  7807. minfo.cbSize = sizeof(MONITORINFO);
  7808. GetMonitorInfo(monitor, &minfo);
  7809. const RECT mr = minfo.rcMonitor;
  7810. const int monitor_w = mr.right - mr.left;
  7811. const int monitor_h = mr.bottom - mr.top;
  7812. const DWORD win_ex_style = WS_EX_APPWINDOW | WS_EX_WINDOWEDGE;
  7813. DWORD win_style;
  7814. RECT rect = { 0, 0, 0, 0 };
  7815. _sapp.fullscreen = fullscreen;
  7816. if (!_sapp.fullscreen) {
  7817. win_style = WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_SIZEBOX;
  7818. rect = _sapp.win32.stored_window_rect;
  7819. } else {
  7820. GetWindowRect(_sapp.win32.hwnd, &_sapp.win32.stored_window_rect);
  7821. win_style = WS_POPUP | WS_SYSMENU | WS_VISIBLE;
  7822. rect.left = mr.left;
  7823. rect.top = mr.top;
  7824. rect.right = rect.left + monitor_w;
  7825. rect.bottom = rect.top + monitor_h;
  7826. AdjustWindowRectEx(&rect, win_style, FALSE, win_ex_style);
  7827. }
  7828. const int win_w = rect.right - rect.left;
  7829. const int win_h = rect.bottom - rect.top;
  7830. const int win_x = rect.left;
  7831. const int win_y = rect.top;
  7832. SetWindowLongPtr(_sapp.win32.hwnd, GWL_STYLE, win_style);
  7833. SetWindowPos(_sapp.win32.hwnd, HWND_TOP, win_x, win_y, win_w, win_h, swp_flags | SWP_FRAMECHANGED);
  7834. }
  7835. _SOKOL_PRIVATE void _sapp_win32_toggle_fullscreen(void) {
  7836. _sapp_win32_set_fullscreen(!_sapp.fullscreen, SWP_SHOWWINDOW);
  7837. }
  7838. _SOKOL_PRIVATE void _sapp_win32_init_cursor(sapp_mouse_cursor cursor) {
  7839. SOKOL_ASSERT((cursor >= 0) && (cursor < _SAPP_MOUSECURSOR_NUM));
  7840. // NOTE: the OCR_* constants are only defined if OEMRESOURCE is defined
  7841. // before windows.h is included, but we can't guarantee that because
  7842. // the sokol_app.h implementation may be included with other implementations
  7843. // in the same compilation unit
  7844. int id = 0;
  7845. switch (cursor) {
  7846. case SAPP_MOUSECURSOR_ARROW: id = 32512; break; // OCR_NORMAL
  7847. case SAPP_MOUSECURSOR_IBEAM: id = 32513; break; // OCR_IBEAM
  7848. case SAPP_MOUSECURSOR_CROSSHAIR: id = 32515; break; // OCR_CROSS
  7849. case SAPP_MOUSECURSOR_POINTING_HAND: id = 32649; break; // OCR_HAND
  7850. case SAPP_MOUSECURSOR_RESIZE_EW: id = 32644; break; // OCR_SIZEWE
  7851. case SAPP_MOUSECURSOR_RESIZE_NS: id = 32645; break; // OCR_SIZENS
  7852. case SAPP_MOUSECURSOR_RESIZE_NWSE: id = 32642; break; // OCR_SIZENWSE
  7853. case SAPP_MOUSECURSOR_RESIZE_NESW: id = 32643; break; // OCR_SIZENESW
  7854. case SAPP_MOUSECURSOR_RESIZE_ALL: id = 32646; break; // OCR_SIZEALL
  7855. case SAPP_MOUSECURSOR_NOT_ALLOWED: id = 32648; break; // OCR_NO
  7856. default: break;
  7857. }
  7858. if (id != 0) {
  7859. _sapp.win32.standard_cursors[cursor] = (HCURSOR)LoadImageW(NULL, MAKEINTRESOURCEW(id), IMAGE_CURSOR, 0, 0, LR_DEFAULTSIZE|LR_SHARED);
  7860. }
  7861. // fallback: default cursor
  7862. if (0 == _sapp.win32.standard_cursors[cursor]) {
  7863. // 32512 => IDC_ARROW
  7864. _sapp.win32.standard_cursors[cursor] = LoadCursorW(NULL, MAKEINTRESOURCEW(32512));
  7865. }
  7866. SOKOL_ASSERT(0 != _sapp.win32.standard_cursors[cursor]);
  7867. }
  7868. _SOKOL_PRIVATE void _sapp_win32_init_cursors(void) {
  7869. for (int i = 0; i < _SAPP_MOUSECURSOR_NUM; i++) {
  7870. _sapp_win32_init_cursor((sapp_mouse_cursor)i);
  7871. }
  7872. }
  7873. _SOKOL_PRIVATE bool _sapp_win32_cursor_in_content_area(void) {
  7874. POINT pos;
  7875. if (!GetCursorPos(&pos)) {
  7876. return false;
  7877. }
  7878. if (WindowFromPoint(pos) != _sapp.win32.hwnd) {
  7879. return false;
  7880. }
  7881. RECT area;
  7882. GetClientRect(_sapp.win32.hwnd, &area);
  7883. ClientToScreen(_sapp.win32.hwnd, (POINT*)&area.left);
  7884. ClientToScreen(_sapp.win32.hwnd, (POINT*)&area.right);
  7885. return PtInRect(&area, pos) == TRUE;
  7886. }
  7887. _SOKOL_PRIVATE void _sapp_win32_update_cursor(sapp_mouse_cursor cursor, bool shown, bool skip_area_test) {
  7888. SOKOL_ASSERT((cursor >= 0) && (cursor < _SAPP_MOUSECURSOR_NUM));
  7889. // NOTE: when called from WM_SETCURSOR, the area test would be redundant
  7890. if (!skip_area_test) {
  7891. if (!_sapp_win32_cursor_in_content_area()) {
  7892. return;
  7893. }
  7894. }
  7895. HCURSOR cursor_handle = NULL;
  7896. if (shown) {
  7897. if (_sapp.custom_cursor_bound[cursor]) {
  7898. SOKOL_ASSERT(_sapp.win32.custom_cursors[cursor]);
  7899. cursor_handle = _sapp.win32.custom_cursors[cursor];
  7900. SOKOL_ASSERT(0 != cursor_handle);
  7901. } else {
  7902. cursor_handle = _sapp.win32.standard_cursors[cursor];
  7903. SOKOL_ASSERT(0 != cursor_handle);
  7904. }
  7905. }
  7906. SetCursor(cursor_handle);
  7907. }
  7908. _SOKOL_PRIVATE void _sapp_win32_capture_mouse(uint8_t btn_mask) {
  7909. if (0 == _sapp.win32.mouse.capture_mask) {
  7910. SetCapture(_sapp.win32.hwnd);
  7911. }
  7912. _sapp.win32.mouse.capture_mask |= btn_mask;
  7913. }
  7914. _SOKOL_PRIVATE void _sapp_win32_release_mouse(uint8_t btn_mask) {
  7915. if (0 != _sapp.win32.mouse.capture_mask) {
  7916. _sapp.win32.mouse.capture_mask &= ~btn_mask;
  7917. if (0 == _sapp.win32.mouse.capture_mask) {
  7918. ReleaseCapture();
  7919. }
  7920. }
  7921. }
  7922. _SOKOL_PRIVATE bool _sapp_win32_is_foreground_window(void) {
  7923. return _sapp.win32.hwnd == GetForegroundWindow();
  7924. }
  7925. _SOKOL_PRIVATE void _sapp_win32_lock_mouse(bool lock) {
  7926. _sapp.win32.mouse.requested_lock = lock;
  7927. }
  7928. _SOKOL_PRIVATE void _sapp_win32_free_raw_input_data(void) {
  7929. if (_sapp.win32.raw_input_data.ptr) {
  7930. _sapp_free(_sapp.win32.raw_input_data.ptr);
  7931. _sapp.win32.raw_input_data.ptr = 0;
  7932. _sapp.win32.raw_input_data.size = 0;
  7933. }
  7934. }
  7935. _SOKOL_PRIVATE void _sapp_win32_alloc_raw_input_data(size_t size) {
  7936. SOKOL_ASSERT(!_sapp.win32.raw_input_data.ptr);
  7937. SOKOL_ASSERT(size > 0);
  7938. _sapp.win32.raw_input_data.ptr = _sapp_malloc(size);
  7939. _sapp.win32.raw_input_data.size = size;
  7940. SOKOL_ASSERT(_sapp.win32.raw_input_data.ptr);
  7941. }
  7942. _SOKOL_PRIVATE void* _sapp_win32_ensure_raw_input_data(size_t required_size) {
  7943. if (required_size > _sapp.win32.raw_input_data.size) {
  7944. _sapp_win32_free_raw_input_data();
  7945. _sapp_win32_alloc_raw_input_data(required_size);
  7946. }
  7947. // we expect that malloc() returns at least 8-byte aligned memory
  7948. SOKOL_ASSERT((((uintptr_t)_sapp.win32.raw_input_data.ptr) & 7) == 0);
  7949. return _sapp.win32.raw_input_data.ptr;
  7950. }
  7951. _SOKOL_PRIVATE void _sapp_win32_do_lock_mouse(void) {
  7952. _sapp.mouse.locked = true;
  7953. // hide mouse cursor (NOTE: this maintains a hidden counter, but since
  7954. // only mouse-lock uses ShowCursor this doesn't matter)
  7955. ShowCursor(FALSE);
  7956. // reset dx/dy and release any active mouse capture
  7957. _sapp.mouse.dx = 0.0f;
  7958. _sapp.mouse.dy = 0.0f;
  7959. _sapp_win32_release_mouse(0xFF);
  7960. // store current mouse position so that it can be restored when unlocked
  7961. POINT pos;
  7962. if (GetCursorPos(&pos)) {
  7963. _sapp.win32.mouse.lock.pos_valid = true;
  7964. _sapp.win32.mouse.lock.pos_x = pos.x;
  7965. _sapp.win32.mouse.lock.pos_y = pos.y;
  7966. } else {
  7967. _sapp.win32.mouse.lock.pos_valid = false;
  7968. }
  7969. // while mouse is locked, restrict cursor movement to the client
  7970. // rectangle so that we don't loose any mouse movement events
  7971. RECT client_rect;
  7972. GetClientRect(_sapp.win32.hwnd, &client_rect);
  7973. POINT mid_point;
  7974. mid_point.x = (client_rect.right - client_rect.left) / 2;
  7975. mid_point.y = (client_rect.bottom - client_rect.top) / 2;
  7976. ClientToScreen(_sapp.win32.hwnd, &mid_point);
  7977. RECT clip_rect;
  7978. clip_rect.left = clip_rect.right = mid_point.x;
  7979. clip_rect.top = clip_rect.bottom = mid_point.y;
  7980. ClipCursor(&clip_rect);
  7981. // enable raw input for mouse, starts sending WM_INPUT messages to WinProc (see GLFW)
  7982. const RAWINPUTDEVICE rid = {
  7983. 0x01, // usUsagePage: HID_USAGE_PAGE_GENERIC
  7984. 0x02, // usUsage: HID_USAGE_GENERIC_MOUSE
  7985. 0, // dwFlags
  7986. _sapp.win32.hwnd // hwndTarget
  7987. };
  7988. if (!RegisterRawInputDevices(&rid, 1, sizeof(rid))) {
  7989. _SAPP_ERROR(WIN32_REGISTER_RAW_INPUT_DEVICES_FAILED_MOUSE_LOCK);
  7990. }
  7991. // in case the raw mouse device only supports absolute position reporting,
  7992. // we need to skip the dx/dy compution for the first WM_INPUT event
  7993. _sapp.win32.mouse.raw_input.pos_valid = false;
  7994. }
  7995. _SOKOL_PRIVATE void _sapp_win32_do_unlock_mouse(void) {
  7996. _sapp.mouse.locked = false;
  7997. // make mouse cursor visible
  7998. ShowCursor(TRUE);
  7999. // reset dx/dy and release any active mouse capture
  8000. _sapp.mouse.dx = 0.0f;
  8001. _sapp.mouse.dy = 0.0f;
  8002. _sapp_win32_release_mouse(0xFF);
  8003. // disable raw input for mouse
  8004. const RAWINPUTDEVICE rid = { 0x01, 0x02, RIDEV_REMOVE, NULL };
  8005. if (!RegisterRawInputDevices(&rid, 1, sizeof(rid))) {
  8006. _SAPP_ERROR(WIN32_REGISTER_RAW_INPUT_DEVICES_FAILED_MOUSE_UNLOCK);
  8007. }
  8008. // unrestrict mouse movement
  8009. ClipCursor(NULL);
  8010. // restore the 'pre-locked' mouse position
  8011. if (_sapp.win32.mouse.lock.pos_valid) {
  8012. SetCursorPos(_sapp.win32.mouse.lock.pos_x, _sapp.win32.mouse.lock.pos_y);
  8013. _sapp.win32.mouse.lock.pos_valid = false;
  8014. }
  8015. }
  8016. _SOKOL_PRIVATE void _sapp_win32_update_mouse_lock(void) {
  8017. // mouse lock can only be active when we're the active window
  8018. if (!_sapp_win32_is_foreground_window()) {
  8019. // unlock mouse if currently locked
  8020. if (_sapp.mouse.locked) {
  8021. _sapp_win32_do_unlock_mouse();
  8022. }
  8023. return;
  8024. }
  8025. // nothing to do if requested lock state matches current lock state
  8026. const bool lock = _sapp.win32.mouse.requested_lock;
  8027. if (lock == _sapp.mouse.locked) {
  8028. return;
  8029. }
  8030. // otherwise change into desired state
  8031. if (lock) {
  8032. _sapp_win32_do_lock_mouse();
  8033. } else {
  8034. _sapp_win32_do_unlock_mouse();
  8035. }
  8036. }
  8037. _SOKOL_PRIVATE bool _sapp_win32_update_monitor(void) {
  8038. const HMONITOR cur_monitor = MonitorFromWindow(_sapp.win32.hwnd, MONITOR_DEFAULTTONULL);
  8039. if (cur_monitor != _sapp.win32.hmonitor) {
  8040. _sapp.win32.hmonitor = cur_monitor;
  8041. return true;
  8042. } else {
  8043. return false;
  8044. }
  8045. }
  8046. _SOKOL_PRIVATE uint32_t _sapp_win32_mods(void) {
  8047. uint32_t mods = 0;
  8048. if (GetKeyState(VK_SHIFT) & (1<<15)) {
  8049. mods |= SAPP_MODIFIER_SHIFT;
  8050. }
  8051. if (GetKeyState(VK_CONTROL) & (1<<15)) {
  8052. mods |= SAPP_MODIFIER_CTRL;
  8053. }
  8054. if (GetKeyState(VK_MENU) & (1<<15)) {
  8055. mods |= SAPP_MODIFIER_ALT;
  8056. }
  8057. if ((GetKeyState(VK_LWIN) | GetKeyState(VK_RWIN)) & (1<<15)) {
  8058. mods |= SAPP_MODIFIER_SUPER;
  8059. }
  8060. const bool swapped = (TRUE == GetSystemMetrics(SM_SWAPBUTTON));
  8061. if (GetAsyncKeyState(VK_LBUTTON)) {
  8062. mods |= swapped ? SAPP_MODIFIER_RMB : SAPP_MODIFIER_LMB;
  8063. }
  8064. if (GetAsyncKeyState(VK_RBUTTON)) {
  8065. mods |= swapped ? SAPP_MODIFIER_LMB : SAPP_MODIFIER_RMB;
  8066. }
  8067. if (GetAsyncKeyState(VK_MBUTTON)) {
  8068. mods |= SAPP_MODIFIER_MMB;
  8069. }
  8070. return mods;
  8071. }
  8072. _SOKOL_PRIVATE void _sapp_win32_mouse_update(LPARAM lParam) {
  8073. if (!_sapp.mouse.locked) {
  8074. const float new_x = (float)GET_X_LPARAM(lParam) * _sapp.win32.dpi.mouse_scale;
  8075. const float new_y = (float)GET_Y_LPARAM(lParam) * _sapp.win32.dpi.mouse_scale;
  8076. if (_sapp.mouse.pos_valid) {
  8077. // don't update dx/dy in the very first event
  8078. _sapp.mouse.dx = new_x - _sapp.mouse.x;
  8079. _sapp.mouse.dy = new_y - _sapp.mouse.y;
  8080. }
  8081. _sapp.mouse.x = new_x;
  8082. _sapp.mouse.y = new_y;
  8083. _sapp.mouse.pos_valid = true;
  8084. }
  8085. }
  8086. _SOKOL_PRIVATE void _sapp_win32_mouse_event(sapp_event_type type, sapp_mousebutton btn) {
  8087. if (_sapp_events_enabled()) {
  8088. _sapp_init_event(type);
  8089. _sapp.event.modifiers = _sapp_win32_mods();
  8090. _sapp.event.mouse_button = btn;
  8091. _sapp_call_event(&_sapp.event);
  8092. }
  8093. }
  8094. _SOKOL_PRIVATE void _sapp_win32_scroll_event(float x, float y) {
  8095. if (_sapp_events_enabled()) {
  8096. _sapp_init_event(SAPP_EVENTTYPE_MOUSE_SCROLL);
  8097. _sapp.event.modifiers = _sapp_win32_mods();
  8098. _sapp.event.scroll_x = -x / 30.0f;
  8099. _sapp.event.scroll_y = y / 30.0f;
  8100. _sapp_call_event(&_sapp.event);
  8101. }
  8102. }
  8103. _SOKOL_PRIVATE void _sapp_win32_key_event(sapp_event_type type, int vk, bool repeat) {
  8104. if (_sapp_events_enabled() && (vk < SAPP_MAX_KEYCODES)) {
  8105. _sapp_init_event(type);
  8106. _sapp.event.modifiers = _sapp_win32_mods();
  8107. _sapp.event.key_code = _sapp.keycodes[vk];
  8108. _sapp.event.key_repeat = repeat;
  8109. _sapp_call_event(&_sapp.event);
  8110. /* check if a CLIPBOARD_PASTED event must be sent too */
  8111. if (_sapp.clipboard.enabled &&
  8112. (type == SAPP_EVENTTYPE_KEY_DOWN) &&
  8113. (_sapp.event.modifiers == SAPP_MODIFIER_CTRL) &&
  8114. (_sapp.event.key_code == SAPP_KEYCODE_V))
  8115. {
  8116. _sapp_init_event(SAPP_EVENTTYPE_CLIPBOARD_PASTED);
  8117. _sapp_call_event(&_sapp.event);
  8118. }
  8119. }
  8120. }
  8121. _SOKOL_PRIVATE void _sapp_win32_char_event(uint32_t c, bool repeat) {
  8122. if (_sapp_events_enabled() && (c >= 32)) {
  8123. if (c >= 0xD800 && c <= 0xDBFF) {
  8124. _sapp.win32.surrogate = (WCHAR)c - 0xD800;
  8125. } else {
  8126. if (c > 0xDC00 && c <= 0xDFFF) {
  8127. c = (uint32_t)(_sapp.win32.surrogate) << 10 | (c - 0xDC00);
  8128. c += 0x10000;
  8129. _sapp.win32.surrogate = 0;
  8130. }
  8131. _sapp_init_event(SAPP_EVENTTYPE_CHAR);
  8132. _sapp.event.modifiers = _sapp_win32_mods();
  8133. _sapp.event.char_code = c;
  8134. _sapp.event.key_repeat = repeat;
  8135. _sapp_call_event(&_sapp.event);
  8136. }
  8137. }
  8138. }
  8139. _SOKOL_PRIVATE void _sapp_win32_dpi_changed(HWND hWnd, LPRECT proposed_win_rect) {
  8140. /* called on WM_DPICHANGED, which will only be sent to the application
  8141. if sapp_desc.high_dpi is true and the Windows version is recent enough
  8142. to support DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2
  8143. */
  8144. SOKOL_ASSERT(_sapp.desc.high_dpi);
  8145. HINSTANCE user32 = LoadLibraryA("user32.dll");
  8146. if (!user32) {
  8147. return;
  8148. }
  8149. typedef UINT(WINAPI * GETDPIFORWINDOW_T)(HWND hwnd);
  8150. GETDPIFORWINDOW_T fn_getdpiforwindow = (GETDPIFORWINDOW_T)(void*)GetProcAddress(user32, "GetDpiForWindow");
  8151. if (fn_getdpiforwindow) {
  8152. UINT dpix = fn_getdpiforwindow(_sapp.win32.hwnd);
  8153. // NOTE: for high-dpi apps, mouse_scale remains one
  8154. _sapp.win32.dpi.window_scale = (float)dpix / 96.0f;
  8155. _sapp.win32.dpi.content_scale = _sapp.win32.dpi.window_scale;
  8156. _sapp.dpi_scale = _sapp.win32.dpi.window_scale;
  8157. SetWindowPos(hWnd, 0,
  8158. proposed_win_rect->left,
  8159. proposed_win_rect->top,
  8160. proposed_win_rect->right - proposed_win_rect->left,
  8161. proposed_win_rect->bottom - proposed_win_rect->top,
  8162. SWP_NOZORDER | SWP_NOACTIVATE);
  8163. }
  8164. FreeLibrary(user32);
  8165. }
  8166. _SOKOL_PRIVATE void _sapp_win32_files_dropped(HDROP hdrop) {
  8167. if (!_sapp.drop.enabled) {
  8168. return;
  8169. }
  8170. _sapp_clear_drop_buffer();
  8171. bool drop_failed = false;
  8172. const int count = (int) DragQueryFileW(hdrop, 0xffffffff, NULL, 0);
  8173. _sapp.drop.num_files = (count > _sapp.drop.max_files) ? _sapp.drop.max_files : count;
  8174. for (UINT i = 0; i < (UINT)_sapp.drop.num_files; i++) {
  8175. const UINT num_chars = DragQueryFileW(hdrop, i, NULL, 0) + 1;
  8176. WCHAR* buffer = (WCHAR*) _sapp_malloc_clear(num_chars * sizeof(WCHAR));
  8177. DragQueryFileW(hdrop, i, buffer, num_chars);
  8178. if (!_sapp_win32_wide_to_utf8(buffer, _sapp_dropped_file_path_ptr((int)i), _sapp.drop.max_path_length)) {
  8179. _SAPP_ERROR(DROPPED_FILE_PATH_TOO_LONG);
  8180. drop_failed = true;
  8181. }
  8182. _sapp_free(buffer);
  8183. }
  8184. DragFinish(hdrop);
  8185. if (!drop_failed) {
  8186. if (_sapp_events_enabled()) {
  8187. _sapp_init_event(SAPP_EVENTTYPE_FILES_DROPPED);
  8188. _sapp.event.modifiers = _sapp_win32_mods();
  8189. _sapp_call_event(&_sapp.event);
  8190. }
  8191. } else {
  8192. _sapp_clear_drop_buffer();
  8193. _sapp.drop.num_files = 0;
  8194. }
  8195. }
  8196. _SOKOL_PRIVATE void _sapp_win32_timing_measure(void) {
  8197. #if defined(SOKOL_D3D11)
  8198. // on D3D11, use the more precise DXGI timestamp
  8199. if (_sapp.d3d11.use_dxgi_frame_stats) {
  8200. _SAPP_STRUCT(DXGI_FRAME_STATISTICS, dxgi_stats);
  8201. HRESULT hr = _sapp_dxgi_GetFrameStatistics(_sapp.d3d11.swap_chain, &dxgi_stats);
  8202. if (SUCCEEDED(hr)) {
  8203. if (dxgi_stats.SyncRefreshCount != _sapp.d3d11.sync_refresh_count) {
  8204. _sapp.d3d11.sync_refresh_count = dxgi_stats.SyncRefreshCount;
  8205. LARGE_INTEGER qpc = dxgi_stats.SyncQPCTime;
  8206. const uint64_t now = (uint64_t)_sapp_int64_muldiv(qpc.QuadPart - _sapp.timing.timestamp.win.start.QuadPart, 1000000000, _sapp.timing.timestamp.win.freq.QuadPart);
  8207. _sapp_timing_external(&_sapp.timing, (double)now / 1000000000.0);
  8208. }
  8209. return;
  8210. }
  8211. }
  8212. // fallback if swap model isn't "flip-discard" or GetFrameStatistics failed for another reason
  8213. _sapp_timing_measure(&_sapp.timing);
  8214. #endif
  8215. #if defined(SOKOL_GLCORE)
  8216. _sapp_timing_measure(&_sapp.timing);
  8217. #endif
  8218. #if defined(SOKOL_NOAPI)
  8219. _sapp_timing_measure(&_sapp.timing);
  8220. #endif
  8221. }
  8222. _SOKOL_PRIVATE void _sapp_win32_frame(bool from_winproc) {
  8223. #if defined(SOKOL_WGPU)
  8224. _sapp_wgpu_frame();
  8225. #else
  8226. _sapp_frame();
  8227. #endif
  8228. #if defined(SOKOL_D3D11)
  8229. bool do_not_wait = from_winproc;
  8230. _sapp_d3d11_present(do_not_wait);
  8231. #endif
  8232. #if defined(SOKOL_GLCORE)
  8233. _sapp_wgl_swap_buffers();
  8234. #endif
  8235. if (!from_winproc) {
  8236. if (IsIconic(_sapp.win32.hwnd)) {
  8237. Sleep((DWORD)(16 * _sapp.swap_interval));
  8238. }
  8239. }
  8240. }
  8241. _SOKOL_PRIVATE LRESULT CALLBACK _sapp_win32_wndproc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
  8242. if (!_sapp.win32.in_create_window) {
  8243. switch (uMsg) {
  8244. case WM_CLOSE:
  8245. /* only give user a chance to intervene when sapp_quit() wasn't already called */
  8246. if (!_sapp.quit_ordered) {
  8247. /* if window should be closed and event handling is enabled, give user code
  8248. a change to intervene via sapp_cancel_quit()
  8249. */
  8250. _sapp.quit_requested = true;
  8251. _sapp_win32_app_event(SAPP_EVENTTYPE_QUIT_REQUESTED);
  8252. /* if user code hasn't intervened, quit the app */
  8253. if (_sapp.quit_requested) {
  8254. _sapp.quit_ordered = true;
  8255. }
  8256. }
  8257. if (_sapp.quit_ordered) {
  8258. PostQuitMessage(0);
  8259. }
  8260. return 0;
  8261. case WM_SYSCOMMAND:
  8262. switch (wParam & 0xFFF0) {
  8263. case SC_SCREENSAVE:
  8264. case SC_MONITORPOWER:
  8265. if (_sapp.fullscreen) {
  8266. /* disable screen saver and blanking in fullscreen mode */
  8267. return 0;
  8268. }
  8269. break;
  8270. case SC_KEYMENU:
  8271. /* user trying to access menu via ALT */
  8272. return 0;
  8273. }
  8274. break;
  8275. case WM_ERASEBKGND:
  8276. return 1;
  8277. case WM_SIZE:
  8278. {
  8279. const bool iconified = wParam == SIZE_MINIMIZED;
  8280. if (iconified != _sapp.win32.iconified) {
  8281. _sapp.win32.iconified = iconified;
  8282. if (iconified) {
  8283. _sapp_win32_app_event(SAPP_EVENTTYPE_ICONIFIED);
  8284. } else {
  8285. _sapp_win32_app_event(SAPP_EVENTTYPE_RESTORED);
  8286. }
  8287. }
  8288. }
  8289. break;
  8290. case WM_SETFOCUS:
  8291. _sapp_win32_app_event(SAPP_EVENTTYPE_FOCUSED);
  8292. break;
  8293. case WM_KILLFOCUS:
  8294. _sapp_win32_app_event(SAPP_EVENTTYPE_UNFOCUSED);
  8295. break;
  8296. case WM_SETCURSOR:
  8297. if (LOWORD(lParam) == HTCLIENT) {
  8298. _sapp_win32_update_cursor(_sapp.mouse.current_cursor, _sapp.mouse.shown, true);
  8299. return TRUE;
  8300. }
  8301. break;
  8302. case WM_DPICHANGED:
  8303. {
  8304. /* Update window's DPI and size if its moved to another monitor with a different DPI
  8305. Only sent if DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2 is used.
  8306. */
  8307. _sapp_win32_dpi_changed(hWnd, (LPRECT)lParam);
  8308. break;
  8309. }
  8310. case WM_LBUTTONDOWN:
  8311. _sapp_win32_mouse_update(lParam);
  8312. _sapp_win32_mouse_event(SAPP_EVENTTYPE_MOUSE_DOWN, SAPP_MOUSEBUTTON_LEFT);
  8313. _sapp_win32_capture_mouse(1<<SAPP_MOUSEBUTTON_LEFT);
  8314. break;
  8315. case WM_RBUTTONDOWN:
  8316. _sapp_win32_mouse_update(lParam);
  8317. _sapp_win32_mouse_event(SAPP_EVENTTYPE_MOUSE_DOWN, SAPP_MOUSEBUTTON_RIGHT);
  8318. _sapp_win32_capture_mouse(1<<SAPP_MOUSEBUTTON_RIGHT);
  8319. break;
  8320. case WM_MBUTTONDOWN:
  8321. _sapp_win32_mouse_update(lParam);
  8322. _sapp_win32_mouse_event(SAPP_EVENTTYPE_MOUSE_DOWN, SAPP_MOUSEBUTTON_MIDDLE);
  8323. _sapp_win32_capture_mouse(1<<SAPP_MOUSEBUTTON_MIDDLE);
  8324. break;
  8325. case WM_LBUTTONUP:
  8326. _sapp_win32_mouse_update(lParam);
  8327. _sapp_win32_mouse_event(SAPP_EVENTTYPE_MOUSE_UP, SAPP_MOUSEBUTTON_LEFT);
  8328. _sapp_win32_release_mouse(1<<SAPP_MOUSEBUTTON_LEFT);
  8329. break;
  8330. case WM_RBUTTONUP:
  8331. _sapp_win32_mouse_update(lParam);
  8332. _sapp_win32_mouse_event(SAPP_EVENTTYPE_MOUSE_UP, SAPP_MOUSEBUTTON_RIGHT);
  8333. _sapp_win32_release_mouse(1<<SAPP_MOUSEBUTTON_RIGHT);
  8334. break;
  8335. case WM_MBUTTONUP:
  8336. _sapp_win32_mouse_update(lParam);
  8337. _sapp_win32_mouse_event(SAPP_EVENTTYPE_MOUSE_UP, SAPP_MOUSEBUTTON_MIDDLE);
  8338. _sapp_win32_release_mouse(1<<SAPP_MOUSEBUTTON_MIDDLE);
  8339. break;
  8340. case WM_MOUSEMOVE:
  8341. if (!_sapp.mouse.locked) {
  8342. _sapp_win32_mouse_update(lParam);
  8343. if (!_sapp.win32.mouse.tracked) {
  8344. _sapp.win32.mouse.tracked = true;
  8345. _SAPP_STRUCT(TRACKMOUSEEVENT, tme);
  8346. tme.cbSize = sizeof(tme);
  8347. tme.dwFlags = TME_LEAVE;
  8348. tme.hwndTrack = _sapp.win32.hwnd;
  8349. TrackMouseEvent(&tme);
  8350. _sapp.mouse.dx = 0.0f;
  8351. _sapp.mouse.dy = 0.0f;
  8352. _sapp_win32_mouse_event(SAPP_EVENTTYPE_MOUSE_ENTER, SAPP_MOUSEBUTTON_INVALID);
  8353. }
  8354. _sapp_win32_mouse_event(SAPP_EVENTTYPE_MOUSE_MOVE, SAPP_MOUSEBUTTON_INVALID);
  8355. }
  8356. break;
  8357. case WM_INPUT:
  8358. /* raw mouse input during mouse-lock */
  8359. if (_sapp.mouse.locked) {
  8360. HRAWINPUT ri = (HRAWINPUT) lParam;
  8361. // see: https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getrawinputdata
  8362. // also see: https://github.com/glfw/glfw/blob/e7ea71be039836da3a98cea55ae5569cb5eb885c/src/win32_window.c#L912-L924
  8363. // first poll for required size to alloc/grow input buffer, then get the actual data
  8364. UINT size = 0;
  8365. GetRawInputData(ri, RID_INPUT, NULL, &size, sizeof(RAWINPUTHEADER));
  8366. void* raw_input_data_ptr = _sapp_win32_ensure_raw_input_data(size);
  8367. if ((UINT)-1 == GetRawInputData(ri, RID_INPUT, raw_input_data_ptr, &size, sizeof(RAWINPUTHEADER))) {
  8368. _SAPP_ERROR(WIN32_GET_RAW_INPUT_DATA_FAILED);
  8369. break;
  8370. }
  8371. const RAWINPUT* raw_mouse_data = (const RAWINPUT*) raw_input_data_ptr;
  8372. if (raw_mouse_data->data.mouse.usFlags & MOUSE_MOVE_ABSOLUTE) {
  8373. /* mouse only reports absolute position
  8374. NOTE: This code is untested and will most likely behave wrong in Remote Desktop sessions.
  8375. (such remote desktop sessions are setting the MOUSE_MOVE_ABSOLUTE flag).
  8376. See: https://github.com/floooh/sokol/issues/806 and
  8377. https://github.com/microsoft/DirectXTK/commit/ef56b63f3739381e451f7a5a5bd2c9779d2a7555)
  8378. */
  8379. LONG new_x = raw_mouse_data->data.mouse.lLastX;
  8380. LONG new_y = raw_mouse_data->data.mouse.lLastY;
  8381. if (_sapp.win32.mouse.raw_input.pos_valid) {
  8382. _sapp.mouse.dx = (float) (new_x - _sapp.win32.mouse.raw_input.pos_x);
  8383. _sapp.mouse.dy = (float) (new_y - _sapp.win32.mouse.raw_input.pos_y);
  8384. }
  8385. _sapp.win32.mouse.raw_input.pos_x = new_x;
  8386. _sapp.win32.mouse.raw_input.pos_y = new_y;
  8387. _sapp.win32.mouse.raw_input.pos_valid = true;
  8388. } else {
  8389. /* mouse reports movement delta (this seems to be the common case) */
  8390. _sapp.mouse.dx = (float) raw_mouse_data->data.mouse.lLastX;
  8391. _sapp.mouse.dy = (float) raw_mouse_data->data.mouse.lLastY;
  8392. }
  8393. _sapp_win32_mouse_event(SAPP_EVENTTYPE_MOUSE_MOVE, SAPP_MOUSEBUTTON_INVALID);
  8394. }
  8395. break;
  8396. case WM_MOUSELEAVE:
  8397. if (!_sapp.mouse.locked) {
  8398. _sapp.mouse.dx = 0.0f;
  8399. _sapp.mouse.dy = 0.0f;
  8400. _sapp.win32.mouse.tracked = false;
  8401. _sapp_win32_mouse_event(SAPP_EVENTTYPE_MOUSE_LEAVE, SAPP_MOUSEBUTTON_INVALID);
  8402. }
  8403. break;
  8404. case WM_MOUSEWHEEL:
  8405. _sapp_win32_scroll_event(0.0f, (float)((SHORT)HIWORD(wParam)));
  8406. break;
  8407. case WM_MOUSEHWHEEL:
  8408. _sapp_win32_scroll_event((float)((SHORT)HIWORD(wParam)), 0.0f);
  8409. break;
  8410. case WM_CHAR:
  8411. _sapp_win32_char_event((uint32_t)wParam, !!(lParam&0x40000000));
  8412. break;
  8413. case WM_KEYDOWN:
  8414. case WM_SYSKEYDOWN:
  8415. _sapp_win32_key_event(SAPP_EVENTTYPE_KEY_DOWN, (int)(HIWORD(lParam)&0x1FF), !!(lParam&0x40000000));
  8416. break;
  8417. case WM_KEYUP:
  8418. case WM_SYSKEYUP:
  8419. _sapp_win32_key_event(SAPP_EVENTTYPE_KEY_UP, (int)(HIWORD(lParam)&0x1FF), false);
  8420. break;
  8421. case WM_ENTERSIZEMOVE:
  8422. SetTimer(_sapp.win32.hwnd, 1, USER_TIMER_MINIMUM, NULL);
  8423. break;
  8424. case WM_EXITSIZEMOVE:
  8425. KillTimer(_sapp.win32.hwnd, 1);
  8426. break;
  8427. case WM_TIMER:
  8428. _sapp_win32_timing_measure();
  8429. _sapp_win32_frame(true);
  8430. /* NOTE: resizing the swap-chain during resize leads to a substantial
  8431. memory spike (hundreds of megabytes for a few seconds).
  8432. if (_sapp_win32_update_dimensions()) {
  8433. #if defined(SOKOL_D3D11)
  8434. _sapp_d3d11_resize_default_render_target();
  8435. #endif
  8436. _sapp_win32_app_event(SAPP_EVENTTYPE_RESIZED);
  8437. }
  8438. */
  8439. break;
  8440. case WM_NCLBUTTONDOWN:
  8441. /* workaround for half-second pause when starting to move window
  8442. see: https://gamedev.net/forums/topic/672094-keeping-things-moving-during-win32-moveresize-events/5254386/
  8443. */
  8444. if (SendMessage(_sapp.win32.hwnd, WM_NCHITTEST, wParam, lParam) == HTCAPTION) {
  8445. POINT point = { 0, 0 };
  8446. if (GetCursorPos(&point)) {
  8447. ScreenToClient(_sapp.win32.hwnd, &point);
  8448. PostMessage(_sapp.win32.hwnd, WM_MOUSEMOVE, 0, ((uint32_t)point.x)|(((uint32_t)point.y) << 16));
  8449. }
  8450. }
  8451. break;
  8452. case WM_DROPFILES:
  8453. _sapp_win32_files_dropped((HDROP)wParam);
  8454. break;
  8455. case WM_DISPLAYCHANGE:
  8456. // refresh rate might have changed
  8457. _sapp_timing_reset(&_sapp.timing);
  8458. break;
  8459. default:
  8460. break;
  8461. }
  8462. }
  8463. return DefWindowProcW(hWnd, uMsg, wParam, lParam);
  8464. }
  8465. _SOKOL_PRIVATE void _sapp_win32_create_window(void) {
  8466. _SAPP_STRUCT(WNDCLASSW, wndclassw);
  8467. wndclassw.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
  8468. wndclassw.lpfnWndProc = (WNDPROC) _sapp_win32_wndproc;
  8469. wndclassw.hInstance = GetModuleHandleW(NULL);
  8470. wndclassw.hCursor = LoadCursor(NULL, IDC_ARROW);
  8471. wndclassw.hIcon = LoadIcon(NULL, IDI_WINLOGO);
  8472. wndclassw.lpszClassName = L"SOKOLAPP";
  8473. RegisterClassW(&wndclassw);
  8474. /* NOTE: regardless whether fullscreen is requested or not, a regular
  8475. windowed-mode window will always be created first (however in hidden
  8476. mode, so that no windowed-mode window pops up before the fullscreen window)
  8477. */
  8478. const DWORD win_ex_style = WS_EX_APPWINDOW | WS_EX_WINDOWEDGE;
  8479. RECT rect = { 0, 0, 0, 0 };
  8480. DWORD win_style = WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_SIZEBOX;
  8481. rect.right = (int) ((float)_sapp.window_width * _sapp.win32.dpi.window_scale);
  8482. rect.bottom = (int) ((float)_sapp.window_height * _sapp.win32.dpi.window_scale);
  8483. const bool use_default_width = 0 == _sapp.window_width;
  8484. const bool use_default_height = 0 == _sapp.window_height;
  8485. AdjustWindowRectEx(&rect, win_style, FALSE, win_ex_style);
  8486. const int win_width = rect.right - rect.left;
  8487. const int win_height = rect.bottom - rect.top;
  8488. _sapp.win32.in_create_window = true;
  8489. _sapp.win32.surrogate = 0;
  8490. _sapp.win32.hwnd = CreateWindowExW(
  8491. win_ex_style, // dwExStyle
  8492. L"SOKOLAPP", // lpClassName
  8493. _sapp.window_title_wide, // lpWindowName
  8494. win_style, // dwStyle
  8495. CW_USEDEFAULT, // X
  8496. SW_HIDE, // Y (NOTE: CW_USEDEFAULT is not used for position here, but internally calls ShowWindow!
  8497. use_default_width ? CW_USEDEFAULT : win_width, // nWidth
  8498. use_default_height ? CW_USEDEFAULT : win_height, // nHeight (NOTE: if width is CW_USEDEFAULT, height is actually ignored)
  8499. NULL, // hWndParent
  8500. NULL, // hMenu
  8501. GetModuleHandle(NULL), // hInstance
  8502. NULL); // lParam
  8503. _sapp.win32.in_create_window = false;
  8504. _sapp.win32.dc = GetDC(_sapp.win32.hwnd);
  8505. _sapp.win32.hmonitor = MonitorFromWindow(_sapp.win32.hwnd, MONITOR_DEFAULTTONULL);
  8506. SOKOL_ASSERT(_sapp.win32.dc);
  8507. /* this will get the actual windowed-mode window size, if fullscreen
  8508. is requested, the set_fullscreen function will then capture the
  8509. current window rectangle, which then might be used later to
  8510. restore the window position when switching back to windowed
  8511. */
  8512. _sapp_win32_update_dimensions();
  8513. if (_sapp.fullscreen) {
  8514. _sapp_win32_set_fullscreen(_sapp.fullscreen, SWP_HIDEWINDOW);
  8515. _sapp_win32_update_dimensions();
  8516. }
  8517. ShowWindow(_sapp.win32.hwnd, SW_SHOW);
  8518. DragAcceptFiles(_sapp.win32.hwnd, 1);
  8519. }
  8520. _SOKOL_PRIVATE void _sapp_win32_destroy_window(void) {
  8521. DestroyWindow(_sapp.win32.hwnd); _sapp.win32.hwnd = 0;
  8522. UnregisterClassW(L"SOKOLAPP", GetModuleHandleW(NULL));
  8523. }
  8524. _SOKOL_PRIVATE void _sapp_win32_destroy_icons(void) {
  8525. if (_sapp.win32.big_icon) {
  8526. DestroyIcon(_sapp.win32.big_icon);
  8527. _sapp.win32.big_icon = 0;
  8528. }
  8529. if (_sapp.win32.small_icon) {
  8530. DestroyIcon(_sapp.win32.small_icon);
  8531. _sapp.win32.small_icon = 0;
  8532. }
  8533. }
  8534. _SOKOL_PRIVATE void _sapp_win32_init_console(void) {
  8535. if (_sapp.desc.win32.console_create || _sapp.desc.win32.console_attach) {
  8536. BOOL con_valid = FALSE;
  8537. if (_sapp.desc.win32.console_attach) {
  8538. con_valid = AttachConsole(ATTACH_PARENT_PROCESS);
  8539. }
  8540. if (!con_valid && _sapp.desc.win32.console_create) {
  8541. con_valid = AllocConsole();
  8542. }
  8543. if (con_valid) {
  8544. FILE* res_fp = 0;
  8545. errno_t err;
  8546. err = freopen_s(&res_fp, "CON", "w", stdout);
  8547. (void)err;
  8548. err = freopen_s(&res_fp, "CON", "w", stderr);
  8549. (void)err;
  8550. }
  8551. }
  8552. if (_sapp.desc.win32.console_utf8) {
  8553. _sapp.win32.orig_codepage = GetConsoleOutputCP();
  8554. SetConsoleOutputCP(CP_UTF8);
  8555. }
  8556. }
  8557. _SOKOL_PRIVATE void _sapp_win32_restore_console(void) {
  8558. if (_sapp.desc.win32.console_utf8) {
  8559. SetConsoleOutputCP(_sapp.win32.orig_codepage);
  8560. }
  8561. }
  8562. _SOKOL_PRIVATE void _sapp_win32_init_dpi(void) {
  8563. DECLARE_HANDLE(DPI_AWARENESS_CONTEXT_T);
  8564. typedef BOOL(WINAPI * SETPROCESSDPIAWARE_T)(void);
  8565. typedef bool (WINAPI * SETPROCESSDPIAWARENESSCONTEXT_T)(DPI_AWARENESS_CONTEXT_T); // since Windows 10, version 1703
  8566. typedef HRESULT(WINAPI * SETPROCESSDPIAWARENESS_T)(PROCESS_DPI_AWARENESS);
  8567. typedef HRESULT(WINAPI * GETDPIFORMONITOR_T)(HMONITOR, MONITOR_DPI_TYPE, UINT*, UINT*);
  8568. SETPROCESSDPIAWARE_T fn_setprocessdpiaware = 0;
  8569. SETPROCESSDPIAWARENESS_T fn_setprocessdpiawareness = 0;
  8570. GETDPIFORMONITOR_T fn_getdpiformonitor = 0;
  8571. SETPROCESSDPIAWARENESSCONTEXT_T fn_setprocessdpiawarenesscontext =0;
  8572. HINSTANCE user32 = LoadLibraryA("user32.dll");
  8573. if (user32) {
  8574. fn_setprocessdpiaware = (SETPROCESSDPIAWARE_T)(void*) GetProcAddress(user32, "SetProcessDPIAware");
  8575. fn_setprocessdpiawarenesscontext = (SETPROCESSDPIAWARENESSCONTEXT_T)(void*) GetProcAddress(user32, "SetProcessDpiAwarenessContext");
  8576. }
  8577. HINSTANCE shcore = LoadLibraryA("shcore.dll");
  8578. if (shcore) {
  8579. fn_setprocessdpiawareness = (SETPROCESSDPIAWARENESS_T)(void*) GetProcAddress(shcore, "SetProcessDpiAwareness");
  8580. fn_getdpiformonitor = (GETDPIFORMONITOR_T)(void*) GetProcAddress(shcore, "GetDpiForMonitor");
  8581. }
  8582. /*
  8583. NOTE on SetProcessDpiAware() vs SetProcessDpiAwareness() vs SetProcessDpiAwarenessContext():
  8584. These are different attempts to get DPI handling on Windows right, from oldest
  8585. to newest. SetProcessDpiAwarenessContext() is required for the new
  8586. DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2 method.
  8587. */
  8588. if (fn_setprocessdpiawareness) {
  8589. if (_sapp.desc.high_dpi) {
  8590. /* app requests HighDPI rendering, first try the Win10 Creator Update per-monitor-dpi awareness,
  8591. if that fails, fall back to system-dpi-awareness
  8592. */
  8593. _sapp.win32.dpi.aware = true;
  8594. DPI_AWARENESS_CONTEXT_T per_monitor_aware_v2 = (DPI_AWARENESS_CONTEXT_T)-4;
  8595. if (!(fn_setprocessdpiawarenesscontext && fn_setprocessdpiawarenesscontext(per_monitor_aware_v2))) {
  8596. // fallback to system-dpi-aware
  8597. fn_setprocessdpiawareness(PROCESS_SYSTEM_DPI_AWARE);
  8598. }
  8599. } else {
  8600. /* if the app didn't request HighDPI rendering, let Windows do the upscaling */
  8601. _sapp.win32.dpi.aware = false;
  8602. fn_setprocessdpiawareness(PROCESS_DPI_UNAWARE);
  8603. }
  8604. } else if (fn_setprocessdpiaware) {
  8605. // fallback for Windows 7
  8606. _sapp.win32.dpi.aware = true;
  8607. fn_setprocessdpiaware();
  8608. }
  8609. /* get dpi scale factor for main monitor */
  8610. if (fn_getdpiformonitor && _sapp.win32.dpi.aware) {
  8611. POINT pt = { 1, 1 };
  8612. HMONITOR hm = MonitorFromPoint(pt, MONITOR_DEFAULTTONEAREST);
  8613. UINT dpix, dpiy;
  8614. HRESULT hr = fn_getdpiformonitor(hm, MDT_EFFECTIVE_DPI, &dpix, &dpiy);
  8615. _SOKOL_UNUSED(hr);
  8616. SOKOL_ASSERT(SUCCEEDED(hr));
  8617. /* clamp window scale to an integer factor */
  8618. _sapp.win32.dpi.window_scale = (float)dpix / 96.0f;
  8619. } else {
  8620. _sapp.win32.dpi.window_scale = 1.0f;
  8621. }
  8622. if (_sapp.desc.high_dpi) {
  8623. _sapp.win32.dpi.content_scale = _sapp.win32.dpi.window_scale;
  8624. _sapp.win32.dpi.mouse_scale = 1.0f;
  8625. } else {
  8626. _sapp.win32.dpi.content_scale = 1.0f;
  8627. _sapp.win32.dpi.mouse_scale = 1.0f / _sapp.win32.dpi.window_scale;
  8628. }
  8629. _sapp.dpi_scale = _sapp.win32.dpi.content_scale;
  8630. if (user32) {
  8631. FreeLibrary(user32);
  8632. }
  8633. if (shcore) {
  8634. FreeLibrary(shcore);
  8635. }
  8636. }
  8637. _SOKOL_PRIVATE bool _sapp_win32_set_clipboard_string(const char* str) {
  8638. SOKOL_ASSERT(str);
  8639. SOKOL_ASSERT(_sapp.win32.hwnd);
  8640. SOKOL_ASSERT(_sapp.clipboard.enabled && (_sapp.clipboard.buf_size > 0));
  8641. if (!OpenClipboard(_sapp.win32.hwnd)) {
  8642. return false;
  8643. }
  8644. HANDLE object = 0;
  8645. wchar_t* wchar_buf = 0;
  8646. const SIZE_T wchar_buf_size = (SIZE_T)_sapp.clipboard.buf_size * sizeof(wchar_t);
  8647. object = GlobalAlloc(GMEM_MOVEABLE, wchar_buf_size);
  8648. if (NULL == object) {
  8649. goto error;
  8650. }
  8651. wchar_buf = (wchar_t*) GlobalLock(object);
  8652. if (NULL == wchar_buf) {
  8653. goto error;
  8654. }
  8655. if (!_sapp_win32_utf8_to_wide(str, wchar_buf, (int)wchar_buf_size)) {
  8656. goto error;
  8657. }
  8658. GlobalUnlock(object);
  8659. wchar_buf = 0;
  8660. EmptyClipboard();
  8661. // NOTE: when successful, SetClipboardData() takes ownership of memory object!
  8662. if (NULL == SetClipboardData(CF_UNICODETEXT, object)) {
  8663. goto error;
  8664. }
  8665. CloseClipboard();
  8666. return true;
  8667. error:
  8668. if (wchar_buf) {
  8669. GlobalUnlock(object);
  8670. }
  8671. if (object) {
  8672. GlobalFree(object);
  8673. }
  8674. CloseClipboard();
  8675. return false;
  8676. }
  8677. _SOKOL_PRIVATE const char* _sapp_win32_get_clipboard_string(void) {
  8678. SOKOL_ASSERT(_sapp.clipboard.enabled && _sapp.clipboard.buffer);
  8679. SOKOL_ASSERT(_sapp.win32.hwnd);
  8680. if (!OpenClipboard(_sapp.win32.hwnd)) {
  8681. /* silently ignore any errors and just return the current
  8682. content of the local clipboard buffer
  8683. */
  8684. return _sapp.clipboard.buffer;
  8685. }
  8686. HANDLE object = GetClipboardData(CF_UNICODETEXT);
  8687. if (!object) {
  8688. CloseClipboard();
  8689. return _sapp.clipboard.buffer;
  8690. }
  8691. const wchar_t* wchar_buf = (const wchar_t*) GlobalLock(object);
  8692. if (!wchar_buf) {
  8693. CloseClipboard();
  8694. return _sapp.clipboard.buffer;
  8695. }
  8696. if (!_sapp_win32_wide_to_utf8(wchar_buf, _sapp.clipboard.buffer, _sapp.clipboard.buf_size)) {
  8697. _SAPP_ERROR(CLIPBOARD_STRING_TOO_BIG);
  8698. }
  8699. GlobalUnlock(object);
  8700. CloseClipboard();
  8701. return _sapp.clipboard.buffer;
  8702. }
  8703. _SOKOL_PRIVATE void _sapp_win32_update_window_title(void) {
  8704. _sapp_win32_utf8_to_wide(_sapp.window_title, _sapp.window_title_wide, sizeof(_sapp.window_title_wide));
  8705. SetWindowTextW(_sapp.win32.hwnd, _sapp.window_title_wide);
  8706. }
  8707. _SOKOL_PRIVATE HICON _sapp_win32_create_icon_from_image(const sapp_image_desc* desc, bool is_cursor) {
  8708. _SAPP_STRUCT(BITMAPV5HEADER, bi);
  8709. bi.bV5Size = sizeof(bi);
  8710. bi.bV5Width = desc->width;
  8711. bi.bV5Height = -desc->height; // NOTE the '-' here to indicate that origin is top-left
  8712. bi.bV5Planes = 1;
  8713. bi.bV5BitCount = 32;
  8714. bi.bV5Compression = BI_BITFIELDS;
  8715. bi.bV5RedMask = 0x00FF0000;
  8716. bi.bV5GreenMask = 0x0000FF00;
  8717. bi.bV5BlueMask = 0x000000FF;
  8718. bi.bV5AlphaMask = 0xFF000000;
  8719. uint8_t* target = 0;
  8720. const uint8_t* source = (const uint8_t*)desc->pixels.ptr;
  8721. HDC dc = GetDC(NULL);
  8722. HBITMAP color = CreateDIBSection(dc, (BITMAPINFO*)&bi, DIB_RGB_COLORS, (void**)&target, NULL, (DWORD)0);
  8723. ReleaseDC(NULL, dc);
  8724. if (0 == color) {
  8725. return NULL;
  8726. }
  8727. SOKOL_ASSERT(target);
  8728. HBITMAP mask = CreateBitmap(desc->width, desc->height, 1, 1, NULL);
  8729. if (0 == mask) {
  8730. DeleteObject(color);
  8731. return NULL;
  8732. }
  8733. for (int i = 0; i < (desc->width*desc->height); i++) {
  8734. target[0] = source[2];
  8735. target[1] = source[1];
  8736. target[2] = source[0];
  8737. target[3] = source[3];
  8738. target += 4;
  8739. source += 4;
  8740. }
  8741. _SAPP_STRUCT(ICONINFO, icon_info);
  8742. icon_info.fIcon = !is_cursor;
  8743. icon_info.xHotspot = (DWORD) (is_cursor ? desc->cursor_hotspot_x : 0);
  8744. icon_info.yHotspot = (DWORD) (is_cursor ? desc->cursor_hotspot_y : 0);
  8745. icon_info.hbmMask = mask;
  8746. icon_info.hbmColor = color;
  8747. HICON icon_handle = CreateIconIndirect(&icon_info);
  8748. DeleteObject(color);
  8749. DeleteObject(mask);
  8750. return icon_handle;
  8751. }
  8752. _SOKOL_PRIVATE void _sapp_win32_set_icon(const sapp_icon_desc* icon_desc, int num_images) {
  8753. SOKOL_ASSERT((num_images > 0) && (num_images <= SAPP_MAX_ICONIMAGES));
  8754. int big_img_index = _sapp_image_bestmatch(icon_desc->images, num_images, GetSystemMetrics(SM_CXICON), GetSystemMetrics(SM_CYICON));
  8755. int sml_img_index = _sapp_image_bestmatch(icon_desc->images, num_images, GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON));
  8756. HICON big_icon = _sapp_win32_create_icon_from_image(&icon_desc->images[big_img_index], false);
  8757. HICON sml_icon = _sapp_win32_create_icon_from_image(&icon_desc->images[sml_img_index], false);
  8758. // if icon creation or lookup has failed for some reason, leave the currently set icon untouched
  8759. if (0 != big_icon) {
  8760. SendMessage(_sapp.win32.hwnd, WM_SETICON, ICON_BIG, (LPARAM) big_icon);
  8761. if (0 != _sapp.win32.big_icon) {
  8762. DestroyIcon(_sapp.win32.big_icon);
  8763. }
  8764. _sapp.win32.big_icon = big_icon;
  8765. }
  8766. if (0 != sml_icon) {
  8767. SendMessage(_sapp.win32.hwnd, WM_SETICON, ICON_SMALL, (LPARAM) sml_icon);
  8768. if (0 != _sapp.win32.small_icon) {
  8769. DestroyIcon(_sapp.win32.small_icon);
  8770. }
  8771. _sapp.win32.small_icon = sml_icon;
  8772. }
  8773. }
  8774. /* don't laugh, but this seems to be the easiest and most robust
  8775. way to check if we're running on Win10
  8776. From: https://github.com/videolan/vlc/blob/232fb13b0d6110c4d1b683cde24cf9a7f2c5c2ea/modules/video_output/win32/d3d11_swapchain.c#L263
  8777. */
  8778. _SOKOL_PRIVATE bool _sapp_win32_is_win10_or_greater(void) {
  8779. HMODULE h = GetModuleHandleW(L"kernel32.dll");
  8780. if (NULL != h) {
  8781. return (NULL != GetProcAddress(h, "GetSystemCpuSetInformation"));
  8782. } else {
  8783. return false;
  8784. }
  8785. }
  8786. _SOKOL_PRIVATE void _sapp_win32_run(const sapp_desc* desc) {
  8787. _sapp_init_state(desc);
  8788. _sapp_win32_init_console();
  8789. _sapp.win32.is_win10_or_greater = _sapp_win32_is_win10_or_greater();
  8790. _sapp_win32_init_keytable();
  8791. _sapp_win32_utf8_to_wide(_sapp.window_title, _sapp.window_title_wide, sizeof(_sapp.window_title_wide));
  8792. _sapp_win32_init_dpi();
  8793. _sapp_win32_init_cursors();
  8794. _sapp_win32_create_window();
  8795. sapp_set_icon(&desc->icon);
  8796. #if defined(SOKOL_D3D11)
  8797. _sapp_d3d11_create_device_and_swapchain();
  8798. _sapp_d3d11_create_default_render_target();
  8799. #elif defined(SOKOL_GLCORE)
  8800. _sapp_wgl_init();
  8801. _sapp_wgl_load_extensions();
  8802. _sapp_wgl_create_context();
  8803. #elif defined(SOKOL_WGPU)
  8804. _sapp_wgpu_init();
  8805. #endif
  8806. _sapp.valid = true;
  8807. bool done = false;
  8808. while (!(done || _sapp.quit_ordered)) {
  8809. _sapp_win32_timing_measure();
  8810. MSG msg;
  8811. while (PeekMessageW(&msg, NULL, 0, 0, PM_REMOVE)) {
  8812. if (WM_QUIT == msg.message) {
  8813. done = true;
  8814. continue;
  8815. } else {
  8816. TranslateMessage(&msg);
  8817. DispatchMessageW(&msg);
  8818. }
  8819. }
  8820. _sapp_win32_frame(false);
  8821. // check for window resized, this cannot happen in WM_SIZE as it explodes memory usage
  8822. if (_sapp_win32_update_dimensions()) {
  8823. #if defined(SOKOL_D3D11)
  8824. _sapp_d3d11_resize_default_render_target();
  8825. #elif defined(SOKOL_WGPU)
  8826. _sapp_wgpu_swapchain_size_changed();
  8827. #endif
  8828. _sapp_win32_app_event(SAPP_EVENTTYPE_RESIZED);
  8829. }
  8830. /* check if the window monitor has changed, need to reset timing because
  8831. the new monitor might have a different refresh rate
  8832. */
  8833. if (_sapp_win32_update_monitor()) {
  8834. _sapp_timing_reset(&_sapp.timing);
  8835. }
  8836. if (_sapp.quit_requested) {
  8837. PostMessage(_sapp.win32.hwnd, WM_CLOSE, 0, 0);
  8838. }
  8839. // update mouse-lock state
  8840. _sapp_win32_update_mouse_lock();
  8841. }
  8842. _sapp_call_cleanup();
  8843. #if defined(SOKOL_D3D11)
  8844. _sapp_d3d11_destroy_default_render_target();
  8845. _sapp_d3d11_destroy_device_and_swapchain();
  8846. #elif defined(SOKOL_GLCORE)
  8847. _sapp_wgl_destroy_context();
  8848. _sapp_wgl_shutdown();
  8849. #elif defined(SOKOL_WGPU)
  8850. _sapp_wgpu_discard();
  8851. #endif
  8852. _sapp_win32_destroy_window();
  8853. _sapp_win32_destroy_icons();
  8854. _sapp_win32_restore_console();
  8855. _sapp_win32_free_raw_input_data();
  8856. _sapp_discard_state();
  8857. }
  8858. _SOKOL_PRIVATE char** _sapp_win32_command_line_to_utf8_argv(LPWSTR w_command_line, int* o_argc) {
  8859. int argc = 0;
  8860. char** argv = 0;
  8861. char* args;
  8862. LPWSTR* w_argv = CommandLineToArgvW(w_command_line, &argc);
  8863. if (w_argv == NULL) {
  8864. // FIXME: chicken egg problem, can't report errors before sokol_main() is called!
  8865. } else {
  8866. size_t size = wcslen(w_command_line) * 4;
  8867. argv = (char**) _sapp_malloc_clear(((size_t)argc + 1) * sizeof(char*) + size);
  8868. SOKOL_ASSERT(argv);
  8869. args = (char*) &argv[argc + 1];
  8870. int n;
  8871. for (int i = 0; i < argc; ++i) {
  8872. n = WideCharToMultiByte(CP_UTF8, 0, w_argv[i], -1, args, (int)size, NULL, NULL);
  8873. if (n == 0) {
  8874. // FIXME: chicken egg problem, can't report errors before sokol_main() is called!
  8875. break;
  8876. }
  8877. argv[i] = args;
  8878. size -= (size_t)n;
  8879. args += n;
  8880. }
  8881. LocalFree(w_argv);
  8882. }
  8883. *o_argc = argc;
  8884. return argv;
  8885. }
  8886. _SOKOL_PRIVATE bool _sapp_win32_make_custom_mouse_cursor(sapp_mouse_cursor cursor, const sapp_image_desc* desc) {
  8887. SOKOL_ASSERT((cursor >= 0) && (cursor < _SAPP_MOUSECURSOR_NUM));
  8888. SOKOL_ASSERT(0 == _sapp.win32.custom_cursors[cursor]);
  8889. const HCURSOR win32_cursor = _sapp_win32_create_icon_from_image(desc, true);
  8890. _sapp.win32.custom_cursors[cursor] = win32_cursor;
  8891. return win32_cursor != 0;
  8892. }
  8893. SOKOL_API_IMPL void _sapp_win32_destroy_custom_mouse_cursor(sapp_mouse_cursor cursor) {
  8894. SOKOL_ASSERT((cursor >= 0) && (cursor < _SAPP_MOUSECURSOR_NUM));
  8895. HCURSOR win32_cursor = _sapp.win32.custom_cursors[cursor];
  8896. SOKOL_ASSERT(win32_cursor);
  8897. _sapp.win32.custom_cursors[cursor] = 0;
  8898. // NOTE: DestroyIcon() may return zero (failure) if the cursor is currently in
  8899. // use. Normally that shouldn't happen since when attempting to unbind the
  8900. // current cursor it will be hidden first, but since there might be other edge
  8901. // cases we just log a warning but don't fail hard
  8902. BOOL res = DestroyIcon(win32_cursor);
  8903. if (!res) {
  8904. _SAPP_WARN(WIN32_DESTROYICON_FOR_CURSOR_FAILED);
  8905. }
  8906. }
  8907. #if !defined(SOKOL_NO_ENTRY)
  8908. #if defined(SOKOL_WIN32_FORCE_MAIN)
  8909. int main(int argc, char* argv[]) {
  8910. sapp_desc desc = sokol_main(argc, argv);
  8911. _sapp_win32_run(&desc);
  8912. return 0;
  8913. }
  8914. #endif /* SOKOL_WIN32_FORCE_MAIN */
  8915. #if defined(SOKOL_WIN32_FORCE_WINMAIN) || !defined(SOKOL_WIN32_FORCE_MAIN)
  8916. int WINAPI WinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPSTR lpCmdLine, _In_ int nCmdShow) {
  8917. _SOKOL_UNUSED(hInstance);
  8918. _SOKOL_UNUSED(hPrevInstance);
  8919. _SOKOL_UNUSED(lpCmdLine);
  8920. _SOKOL_UNUSED(nCmdShow);
  8921. int argc_utf8 = 0;
  8922. char** argv_utf8 = _sapp_win32_command_line_to_utf8_argv(GetCommandLineW(), &argc_utf8);
  8923. sapp_desc desc = sokol_main(argc_utf8, argv_utf8);
  8924. _sapp_win32_run(&desc);
  8925. _sapp_free(argv_utf8);
  8926. return 0;
  8927. }
  8928. #endif /* SOKOL_WIN32_FORCE_WINMAIN */
  8929. #endif /* SOKOL_NO_ENTRY */
  8930. #ifdef _MSC_VER
  8931. #pragma warning(pop)
  8932. #endif
  8933. #endif /* _SAPP_WIN32 */
  8934. // █████ ███ ██ ██████ ██████ ██████ ██ ██████
  8935. // ██ ██ ████ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
  8936. // ███████ ██ ██ ██ ██ ██ ██████ ██ ██ ██ ██ ██
  8937. // ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
  8938. // ██ ██ ██ ████ ██████ ██ ██ ██████ ██ ██████
  8939. //
  8940. // >>android
  8941. #if defined(_SAPP_ANDROID)
  8942. /* android loop thread */
  8943. _SOKOL_PRIVATE bool _sapp_android_init_egl(void) {
  8944. SOKOL_ASSERT(_sapp.android.display == EGL_NO_DISPLAY);
  8945. SOKOL_ASSERT(_sapp.android.context == EGL_NO_CONTEXT);
  8946. EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
  8947. if (display == EGL_NO_DISPLAY) {
  8948. return false;
  8949. }
  8950. if (eglInitialize(display, NULL, NULL) == EGL_FALSE) {
  8951. return false;
  8952. }
  8953. EGLint alpha_size = _sapp.desc.alpha ? 8 : 0;
  8954. const EGLint cfg_attributes[] = {
  8955. EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
  8956. EGL_RENDERABLE_TYPE, EGL_OPENGL_ES3_BIT,
  8957. EGL_RED_SIZE, 8,
  8958. EGL_GREEN_SIZE, 8,
  8959. EGL_BLUE_SIZE, 8,
  8960. EGL_ALPHA_SIZE, alpha_size,
  8961. EGL_DEPTH_SIZE, 16,
  8962. EGL_STENCIL_SIZE, 0,
  8963. EGL_NONE,
  8964. };
  8965. EGLConfig available_cfgs[32];
  8966. EGLint cfg_count;
  8967. eglChooseConfig(display, cfg_attributes, available_cfgs, 32, &cfg_count);
  8968. SOKOL_ASSERT(cfg_count > 0);
  8969. SOKOL_ASSERT(cfg_count <= 32);
  8970. /* find config with 8-bit rgb buffer if available, ndk sample does not trust egl spec */
  8971. EGLConfig config;
  8972. bool exact_cfg_found = false;
  8973. for (int i = 0; i < cfg_count; ++i) {
  8974. EGLConfig c = available_cfgs[i];
  8975. EGLint r, g, b, a, d;
  8976. if (eglGetConfigAttrib(display, c, EGL_RED_SIZE, &r) == EGL_TRUE &&
  8977. eglGetConfigAttrib(display, c, EGL_GREEN_SIZE, &g) == EGL_TRUE &&
  8978. eglGetConfigAttrib(display, c, EGL_BLUE_SIZE, &b) == EGL_TRUE &&
  8979. eglGetConfigAttrib(display, c, EGL_ALPHA_SIZE, &a) == EGL_TRUE &&
  8980. eglGetConfigAttrib(display, c, EGL_DEPTH_SIZE, &d) == EGL_TRUE &&
  8981. r == 8 && g == 8 && b == 8 && (alpha_size == 0 || a == alpha_size) && d == 16) {
  8982. exact_cfg_found = true;
  8983. config = c;
  8984. break;
  8985. }
  8986. }
  8987. if (!exact_cfg_found) {
  8988. config = available_cfgs[0];
  8989. }
  8990. EGLint ctx_attributes[] = {
  8991. EGL_CONTEXT_MAJOR_VERSION, _sapp.desc.gl.major_version,
  8992. EGL_CONTEXT_MINOR_VERSION, _sapp.desc.gl.minor_version,
  8993. EGL_NONE,
  8994. };
  8995. EGLContext context = eglCreateContext(display, config, EGL_NO_CONTEXT, ctx_attributes);
  8996. if (context == EGL_NO_CONTEXT) {
  8997. return false;
  8998. }
  8999. _sapp.android.config = config;
  9000. _sapp.android.display = display;
  9001. _sapp.android.context = context;
  9002. return true;
  9003. }
  9004. _SOKOL_PRIVATE void _sapp_android_cleanup_egl(void) {
  9005. if (_sapp.android.display != EGL_NO_DISPLAY) {
  9006. eglMakeCurrent(_sapp.android.display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
  9007. if (_sapp.android.surface != EGL_NO_SURFACE) {
  9008. eglDestroySurface(_sapp.android.display, _sapp.android.surface);
  9009. _sapp.android.surface = EGL_NO_SURFACE;
  9010. }
  9011. if (_sapp.android.context != EGL_NO_CONTEXT) {
  9012. eglDestroyContext(_sapp.android.display, _sapp.android.context);
  9013. _sapp.android.context = EGL_NO_CONTEXT;
  9014. }
  9015. eglTerminate(_sapp.android.display);
  9016. _sapp.android.display = EGL_NO_DISPLAY;
  9017. }
  9018. }
  9019. _SOKOL_PRIVATE bool _sapp_android_init_egl_surface(ANativeWindow* window) {
  9020. SOKOL_ASSERT(_sapp.android.display != EGL_NO_DISPLAY);
  9021. SOKOL_ASSERT(_sapp.android.context != EGL_NO_CONTEXT);
  9022. SOKOL_ASSERT(_sapp.android.surface == EGL_NO_SURFACE);
  9023. SOKOL_ASSERT(window);
  9024. /* TODO: set window flags */
  9025. /* ANativeActivity_setWindowFlags(activity, AWINDOW_FLAG_KEEP_SCREEN_ON, 0); */
  9026. /* create egl surface and make it current */
  9027. EGLSurface surface = eglCreateWindowSurface(_sapp.android.display, _sapp.android.config, window, NULL);
  9028. if (surface == EGL_NO_SURFACE) {
  9029. return false;
  9030. }
  9031. if (eglMakeCurrent(_sapp.android.display, surface, surface, _sapp.android.context) == EGL_FALSE) {
  9032. return false;
  9033. }
  9034. _sapp.android.surface = surface;
  9035. glGetIntegerv(GL_FRAMEBUFFER_BINDING, (GLint*)&_sapp.gl.framebuffer);
  9036. return true;
  9037. }
  9038. _SOKOL_PRIVATE void _sapp_android_cleanup_egl_surface(void) {
  9039. if (_sapp.android.display == EGL_NO_DISPLAY) {
  9040. return;
  9041. }
  9042. eglMakeCurrent(_sapp.android.display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
  9043. if (_sapp.android.surface != EGL_NO_SURFACE) {
  9044. eglDestroySurface(_sapp.android.display, _sapp.android.surface);
  9045. _sapp.android.surface = EGL_NO_SURFACE;
  9046. }
  9047. }
  9048. _SOKOL_PRIVATE void _sapp_android_app_event(sapp_event_type type) {
  9049. if (_sapp_events_enabled()) {
  9050. _sapp_init_event(type);
  9051. _sapp_call_event(&_sapp.event);
  9052. }
  9053. }
  9054. _SOKOL_PRIVATE void _sapp_android_update_dimensions(ANativeWindow* window, bool force_update) {
  9055. SOKOL_ASSERT(_sapp.android.display != EGL_NO_DISPLAY);
  9056. SOKOL_ASSERT(_sapp.android.context != EGL_NO_CONTEXT);
  9057. SOKOL_ASSERT(_sapp.android.surface != EGL_NO_SURFACE);
  9058. SOKOL_ASSERT(window);
  9059. const int32_t win_w = ANativeWindow_getWidth(window);
  9060. const int32_t win_h = ANativeWindow_getHeight(window);
  9061. SOKOL_ASSERT(win_w >= 0 && win_h >= 0);
  9062. const bool win_changed = (win_w != _sapp.window_width) || (win_h != _sapp.window_height);
  9063. _sapp.window_width = win_w;
  9064. _sapp.window_height = win_h;
  9065. if (win_changed || force_update) {
  9066. if (!_sapp.desc.high_dpi) {
  9067. const int32_t buf_w = win_w / 2;
  9068. const int32_t buf_h = win_h / 2;
  9069. EGLint format;
  9070. EGLBoolean egl_result = eglGetConfigAttrib(_sapp.android.display, _sapp.android.config, EGL_NATIVE_VISUAL_ID, &format);
  9071. SOKOL_ASSERT(egl_result == EGL_TRUE); _SOKOL_UNUSED(egl_result);
  9072. /* NOTE: calling ANativeWindow_setBuffersGeometry() with the same dimensions
  9073. as the ANativeWindow size results in weird display artefacts, that's
  9074. why it's only called when the buffer geometry is different from
  9075. the window size
  9076. */
  9077. int32_t result = ANativeWindow_setBuffersGeometry(window, buf_w, buf_h, format);
  9078. SOKOL_ASSERT(result == 0); _SOKOL_UNUSED(result);
  9079. }
  9080. }
  9081. /* query surface size */
  9082. EGLint fb_w, fb_h;
  9083. EGLBoolean egl_result_w = eglQuerySurface(_sapp.android.display, _sapp.android.surface, EGL_WIDTH, &fb_w);
  9084. EGLBoolean egl_result_h = eglQuerySurface(_sapp.android.display, _sapp.android.surface, EGL_HEIGHT, &fb_h);
  9085. SOKOL_ASSERT(egl_result_w == EGL_TRUE); _SOKOL_UNUSED(egl_result_w);
  9086. SOKOL_ASSERT(egl_result_h == EGL_TRUE); _SOKOL_UNUSED(egl_result_h);
  9087. const bool fb_changed = (fb_w != _sapp.framebuffer_width) || (fb_h != _sapp.framebuffer_height);
  9088. _sapp.framebuffer_width = fb_w;
  9089. _sapp.framebuffer_height = fb_h;
  9090. _sapp.dpi_scale = (float)_sapp.framebuffer_width / (float)_sapp.window_width;
  9091. if (win_changed || fb_changed || force_update) {
  9092. if (!_sapp.first_frame) {
  9093. _sapp_android_app_event(SAPP_EVENTTYPE_RESIZED);
  9094. }
  9095. }
  9096. }
  9097. _SOKOL_PRIVATE void _sapp_android_cleanup(void) {
  9098. if (_sapp.android.surface != EGL_NO_SURFACE) {
  9099. /* egl context is bound, cleanup gracefully */
  9100. if (_sapp.init_called && !_sapp.cleanup_called) {
  9101. _sapp_call_cleanup();
  9102. }
  9103. }
  9104. /* always try to cleanup by destroying egl context */
  9105. _sapp_android_cleanup_egl();
  9106. }
  9107. _SOKOL_PRIVATE void _sapp_android_shutdown(void) {
  9108. /* try to cleanup while we still have a surface and can call cleanup_cb() */
  9109. _sapp_android_cleanup();
  9110. /* request exit */
  9111. ANativeActivity_finish(_sapp.android.activity);
  9112. }
  9113. _SOKOL_PRIVATE void _sapp_android_frame(void) {
  9114. SOKOL_ASSERT(_sapp.android.display != EGL_NO_DISPLAY);
  9115. SOKOL_ASSERT(_sapp.android.context != EGL_NO_CONTEXT);
  9116. SOKOL_ASSERT(_sapp.android.surface != EGL_NO_SURFACE);
  9117. _sapp_timing_measure(&_sapp.timing);
  9118. _sapp_android_update_dimensions(_sapp.android.current.window, false);
  9119. _sapp_frame();
  9120. eglSwapBuffers(_sapp.android.display, _sapp.android.surface);
  9121. }
  9122. _SOKOL_PRIVATE bool _sapp_android_touch_event(const AInputEvent* e) {
  9123. if (AInputEvent_getType(e) != AINPUT_EVENT_TYPE_MOTION) {
  9124. return false;
  9125. }
  9126. if (!_sapp_events_enabled()) {
  9127. return false;
  9128. }
  9129. int32_t action_idx = AMotionEvent_getAction(e);
  9130. int32_t action = action_idx & AMOTION_EVENT_ACTION_MASK;
  9131. sapp_event_type type = SAPP_EVENTTYPE_INVALID;
  9132. switch (action) {
  9133. case AMOTION_EVENT_ACTION_DOWN:
  9134. case AMOTION_EVENT_ACTION_POINTER_DOWN:
  9135. type = SAPP_EVENTTYPE_TOUCHES_BEGAN;
  9136. break;
  9137. case AMOTION_EVENT_ACTION_MOVE:
  9138. type = SAPP_EVENTTYPE_TOUCHES_MOVED;
  9139. break;
  9140. case AMOTION_EVENT_ACTION_UP:
  9141. case AMOTION_EVENT_ACTION_POINTER_UP:
  9142. type = SAPP_EVENTTYPE_TOUCHES_ENDED;
  9143. break;
  9144. case AMOTION_EVENT_ACTION_CANCEL:
  9145. type = SAPP_EVENTTYPE_TOUCHES_CANCELLED;
  9146. break;
  9147. default:
  9148. break;
  9149. }
  9150. if (type == SAPP_EVENTTYPE_INVALID) {
  9151. return false;
  9152. }
  9153. int32_t idx = action_idx >> AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT;
  9154. _sapp_init_event(type);
  9155. _sapp.event.num_touches = (int)AMotionEvent_getPointerCount(e);
  9156. if (_sapp.event.num_touches > SAPP_MAX_TOUCHPOINTS) {
  9157. _sapp.event.num_touches = SAPP_MAX_TOUCHPOINTS;
  9158. }
  9159. for (int32_t i = 0; i < _sapp.event.num_touches; i++) {
  9160. sapp_touchpoint* dst = &_sapp.event.touches[i];
  9161. dst->identifier = (uintptr_t)AMotionEvent_getPointerId(e, (size_t)i);
  9162. dst->pos_x = (AMotionEvent_getX(e, (size_t)i) / _sapp.window_width) * _sapp.framebuffer_width;
  9163. dst->pos_y = (AMotionEvent_getY(e, (size_t)i) / _sapp.window_height) * _sapp.framebuffer_height;
  9164. dst->android_tooltype = (sapp_android_tooltype) AMotionEvent_getToolType(e, (size_t)i);
  9165. if (action == AMOTION_EVENT_ACTION_POINTER_DOWN ||
  9166. action == AMOTION_EVENT_ACTION_POINTER_UP) {
  9167. dst->changed = (i == idx);
  9168. } else {
  9169. dst->changed = true;
  9170. }
  9171. }
  9172. _sapp_call_event(&_sapp.event);
  9173. return true;
  9174. }
  9175. _SOKOL_PRIVATE bool _sapp_android_key_event(const AInputEvent* e) {
  9176. if (AInputEvent_getType(e) != AINPUT_EVENT_TYPE_KEY) {
  9177. return false;
  9178. }
  9179. if (AKeyEvent_getKeyCode(e) == AKEYCODE_BACK) {
  9180. /* FIXME: this should be hooked into a "really quit?" mechanism
  9181. so the app can ask the user for confirmation, this is currently
  9182. generally missing in sokol_app.h
  9183. */
  9184. _sapp_android_shutdown();
  9185. return true;
  9186. }
  9187. return false;
  9188. }
  9189. _SOKOL_PRIVATE int _sapp_android_input_cb(int fd, int events, void* data) {
  9190. _SOKOL_UNUSED(fd);
  9191. _SOKOL_UNUSED(data);
  9192. if ((events & ALOOPER_EVENT_INPUT) == 0) {
  9193. _SAPP_ERROR(ANDROID_UNSUPPORTED_INPUT_EVENT_INPUT_CB);
  9194. return 1;
  9195. }
  9196. SOKOL_ASSERT(_sapp.android.current.input);
  9197. AInputEvent* event = NULL;
  9198. while (AInputQueue_getEvent(_sapp.android.current.input, &event) >= 0) {
  9199. if (AInputQueue_preDispatchEvent(_sapp.android.current.input, event) != 0) {
  9200. continue;
  9201. }
  9202. int32_t handled = 0;
  9203. if (_sapp_android_touch_event(event) || _sapp_android_key_event(event)) {
  9204. handled = 1;
  9205. }
  9206. AInputQueue_finishEvent(_sapp.android.current.input, event, handled);
  9207. }
  9208. return 1;
  9209. }
  9210. _SOKOL_PRIVATE int _sapp_android_main_cb(int fd, int events, void* data) {
  9211. _SOKOL_UNUSED(data);
  9212. if ((events & ALOOPER_EVENT_INPUT) == 0) {
  9213. _SAPP_ERROR(ANDROID_UNSUPPORTED_INPUT_EVENT_MAIN_CB);
  9214. return 1;
  9215. }
  9216. _sapp_android_msg_t msg;
  9217. if (read(fd, &msg, sizeof(msg)) != sizeof(msg)) {
  9218. _SAPP_ERROR(ANDROID_READ_MSG_FAILED);
  9219. return 1;
  9220. }
  9221. pthread_mutex_lock(&_sapp.android.pt.mutex);
  9222. switch (msg) {
  9223. case _SOKOL_ANDROID_MSG_CREATE:
  9224. {
  9225. _SAPP_INFO(ANDROID_MSG_CREATE);
  9226. SOKOL_ASSERT(!_sapp.valid);
  9227. bool result = _sapp_android_init_egl();
  9228. SOKOL_ASSERT(result); _SOKOL_UNUSED(result);
  9229. _sapp.valid = true;
  9230. _sapp.android.has_created = true;
  9231. }
  9232. break;
  9233. case _SOKOL_ANDROID_MSG_RESUME:
  9234. _SAPP_INFO(ANDROID_MSG_RESUME);
  9235. _sapp.android.has_resumed = true;
  9236. _sapp_android_app_event(SAPP_EVENTTYPE_RESUMED);
  9237. break;
  9238. case _SOKOL_ANDROID_MSG_PAUSE:
  9239. _SAPP_INFO(ANDROID_MSG_PAUSE);
  9240. _sapp.android.has_resumed = false;
  9241. _sapp_android_app_event(SAPP_EVENTTYPE_SUSPENDED);
  9242. break;
  9243. case _SOKOL_ANDROID_MSG_FOCUS:
  9244. _SAPP_INFO(ANDROID_MSG_FOCUS);
  9245. _sapp.android.has_focus = true;
  9246. break;
  9247. case _SOKOL_ANDROID_MSG_NO_FOCUS:
  9248. _SAPP_INFO(ANDROID_MSG_NO_FOCUS);
  9249. _sapp.android.has_focus = false;
  9250. break;
  9251. case _SOKOL_ANDROID_MSG_SET_NATIVE_WINDOW:
  9252. _SAPP_INFO(ANDROID_MSG_SET_NATIVE_WINDOW);
  9253. if (_sapp.android.current.window != _sapp.android.pending.window) {
  9254. if (_sapp.android.current.window != NULL) {
  9255. _sapp_android_cleanup_egl_surface();
  9256. }
  9257. if (_sapp.android.pending.window != NULL) {
  9258. if (_sapp_android_init_egl_surface(_sapp.android.pending.window)) {
  9259. _sapp_android_update_dimensions(_sapp.android.pending.window, true);
  9260. } else {
  9261. _sapp_android_shutdown();
  9262. }
  9263. }
  9264. }
  9265. _sapp.android.current.window = _sapp.android.pending.window;
  9266. break;
  9267. case _SOKOL_ANDROID_MSG_SET_INPUT_QUEUE:
  9268. _SAPP_INFO(ANDROID_MSG_SET_INPUT_QUEUE);
  9269. if (_sapp.android.current.input != _sapp.android.pending.input) {
  9270. if (_sapp.android.current.input != NULL) {
  9271. AInputQueue_detachLooper(_sapp.android.current.input);
  9272. }
  9273. if (_sapp.android.pending.input != NULL) {
  9274. AInputQueue_attachLooper(
  9275. _sapp.android.pending.input,
  9276. _sapp.android.looper,
  9277. ALOOPER_POLL_CALLBACK,
  9278. _sapp_android_input_cb,
  9279. NULL); /* data */
  9280. }
  9281. }
  9282. _sapp.android.current.input = _sapp.android.pending.input;
  9283. break;
  9284. case _SOKOL_ANDROID_MSG_DESTROY:
  9285. _SAPP_INFO(ANDROID_MSG_DESTROY);
  9286. _sapp_android_cleanup();
  9287. _sapp.valid = false;
  9288. _sapp.android.is_thread_stopping = true;
  9289. break;
  9290. default:
  9291. _SAPP_WARN(ANDROID_UNKNOWN_MSG);
  9292. break;
  9293. }
  9294. pthread_cond_broadcast(&_sapp.android.pt.cond); /* signal "received" */
  9295. pthread_mutex_unlock(&_sapp.android.pt.mutex);
  9296. return 1;
  9297. }
  9298. _SOKOL_PRIVATE bool _sapp_android_should_update(void) {
  9299. bool is_in_front = _sapp.android.has_resumed && _sapp.android.has_focus;
  9300. bool has_surface = _sapp.android.surface != EGL_NO_SURFACE;
  9301. return is_in_front && has_surface;
  9302. }
  9303. _SOKOL_PRIVATE void _sapp_android_show_keyboard(bool shown) {
  9304. SOKOL_ASSERT(_sapp.valid);
  9305. /* This seems to be broken in the NDK, but there is (a very cumbersome) workaround... */
  9306. if (shown) {
  9307. ANativeActivity_showSoftInput(_sapp.android.activity, ANATIVEACTIVITY_SHOW_SOFT_INPUT_FORCED);
  9308. } else {
  9309. ANativeActivity_hideSoftInput(_sapp.android.activity, ANATIVEACTIVITY_HIDE_SOFT_INPUT_NOT_ALWAYS);
  9310. }
  9311. }
  9312. _SOKOL_PRIVATE void* _sapp_android_loop(void* arg) {
  9313. _SOKOL_UNUSED(arg);
  9314. _SAPP_INFO(ANDROID_LOOP_THREAD_STARTED);
  9315. _sapp.android.looper = ALooper_prepare(0 /* or ALOOPER_PREPARE_ALLOW_NON_CALLBACKS*/);
  9316. ALooper_addFd(_sapp.android.looper,
  9317. _sapp.android.pt.read_from_main_fd,
  9318. ALOOPER_POLL_CALLBACK,
  9319. ALOOPER_EVENT_INPUT,
  9320. _sapp_android_main_cb,
  9321. NULL); /* data */
  9322. /* signal start to main thread */
  9323. pthread_mutex_lock(&_sapp.android.pt.mutex);
  9324. _sapp.android.is_thread_started = true;
  9325. pthread_cond_broadcast(&_sapp.android.pt.cond);
  9326. pthread_mutex_unlock(&_sapp.android.pt.mutex);
  9327. /* main loop */
  9328. while (!_sapp.android.is_thread_stopping) {
  9329. /* sokol frame */
  9330. if (_sapp_android_should_update()) {
  9331. _sapp_android_frame();
  9332. }
  9333. /* process all events (or stop early if app is requested to quit) */
  9334. bool process_events = true;
  9335. while (process_events && !_sapp.android.is_thread_stopping) {
  9336. bool block_until_event = !_sapp.android.is_thread_stopping && !_sapp_android_should_update();
  9337. process_events = ALooper_pollOnce(block_until_event ? -1 : 0, NULL, NULL, NULL) == ALOOPER_POLL_CALLBACK;
  9338. }
  9339. }
  9340. /* cleanup thread */
  9341. if (_sapp.android.current.input != NULL) {
  9342. AInputQueue_detachLooper(_sapp.android.current.input);
  9343. }
  9344. /* the following causes heap corruption on exit, why??
  9345. ALooper_removeFd(_sapp.android.looper, _sapp.android.pt.read_from_main_fd);
  9346. ALooper_release(_sapp.android.looper);*/
  9347. /* signal "destroyed" */
  9348. pthread_mutex_lock(&_sapp.android.pt.mutex);
  9349. _sapp.android.is_thread_stopped = true;
  9350. pthread_cond_broadcast(&_sapp.android.pt.cond);
  9351. pthread_mutex_unlock(&_sapp.android.pt.mutex);
  9352. _SAPP_INFO(ANDROID_LOOP_THREAD_DONE);
  9353. return NULL;
  9354. }
  9355. /* android main/ui thread */
  9356. _SOKOL_PRIVATE void _sapp_android_msg(_sapp_android_msg_t msg) {
  9357. if (write(_sapp.android.pt.write_from_main_fd, &msg, sizeof(msg)) != sizeof(msg)) {
  9358. _SAPP_ERROR(ANDROID_WRITE_MSG_FAILED);
  9359. }
  9360. }
  9361. _SOKOL_PRIVATE void _sapp_android_on_start(ANativeActivity* activity) {
  9362. _SOKOL_UNUSED(activity);
  9363. _SAPP_INFO(ANDROID_NATIVE_ACTIVITY_ONSTART);
  9364. }
  9365. _SOKOL_PRIVATE void _sapp_android_on_resume(ANativeActivity* activity) {
  9366. _SOKOL_UNUSED(activity);
  9367. _SAPP_INFO(ANDROID_NATIVE_ACTIVITY_ONRESUME);
  9368. _sapp_android_msg(_SOKOL_ANDROID_MSG_RESUME);
  9369. }
  9370. _SOKOL_PRIVATE void* _sapp_android_on_save_instance_state(ANativeActivity* activity, size_t* out_size) {
  9371. _SOKOL_UNUSED(activity);
  9372. _SAPP_INFO(ANDROID_NATIVE_ACTIVITY_ONSAVEINSTANCESTATE);
  9373. *out_size = 0;
  9374. return NULL;
  9375. }
  9376. _SOKOL_PRIVATE void _sapp_android_on_window_focus_changed(ANativeActivity* activity, int has_focus) {
  9377. _SOKOL_UNUSED(activity);
  9378. _SAPP_INFO(ANDROID_NATIVE_ACTIVITY_ONWINDOWFOCUSCHANGED);
  9379. if (has_focus) {
  9380. _sapp_android_msg(_SOKOL_ANDROID_MSG_FOCUS);
  9381. } else {
  9382. _sapp_android_msg(_SOKOL_ANDROID_MSG_NO_FOCUS);
  9383. }
  9384. }
  9385. _SOKOL_PRIVATE void _sapp_android_on_pause(ANativeActivity* activity) {
  9386. _SOKOL_UNUSED(activity);
  9387. _SAPP_INFO(ANDROID_NATIVE_ACTIVITY_ONPAUSE);
  9388. _sapp_android_msg(_SOKOL_ANDROID_MSG_PAUSE);
  9389. }
  9390. _SOKOL_PRIVATE void _sapp_android_on_stop(ANativeActivity* activity) {
  9391. _SOKOL_UNUSED(activity);
  9392. _SAPP_INFO(ANDROID_NATIVE_ACTIVITY_ONSTOP);
  9393. }
  9394. _SOKOL_PRIVATE void _sapp_android_msg_set_native_window(ANativeWindow* window) {
  9395. pthread_mutex_lock(&_sapp.android.pt.mutex);
  9396. _sapp.android.pending.window = window;
  9397. _sapp_android_msg(_SOKOL_ANDROID_MSG_SET_NATIVE_WINDOW);
  9398. while (_sapp.android.current.window != window) {
  9399. pthread_cond_wait(&_sapp.android.pt.cond, &_sapp.android.pt.mutex);
  9400. }
  9401. pthread_mutex_unlock(&_sapp.android.pt.mutex);
  9402. }
  9403. _SOKOL_PRIVATE void _sapp_android_on_native_window_created(ANativeActivity* activity, ANativeWindow* window) {
  9404. _SOKOL_UNUSED(activity);
  9405. _SAPP_INFO(ANDROID_NATIVE_ACTIVITY_ONNATIVEWINDOWCREATED);
  9406. _sapp_android_msg_set_native_window(window);
  9407. }
  9408. _SOKOL_PRIVATE void _sapp_android_on_native_window_destroyed(ANativeActivity* activity, ANativeWindow* window) {
  9409. _SOKOL_UNUSED(activity);
  9410. _SOKOL_UNUSED(window);
  9411. _SAPP_INFO(ANDROID_NATIVE_ACTIVITY_ONNATIVEWINDOWDESTROYED);
  9412. _sapp_android_msg_set_native_window(NULL);
  9413. }
  9414. _SOKOL_PRIVATE void _sapp_android_msg_set_input_queue(AInputQueue* input) {
  9415. pthread_mutex_lock(&_sapp.android.pt.mutex);
  9416. _sapp.android.pending.input = input;
  9417. _sapp_android_msg(_SOKOL_ANDROID_MSG_SET_INPUT_QUEUE);
  9418. while (_sapp.android.current.input != input) {
  9419. pthread_cond_wait(&_sapp.android.pt.cond, &_sapp.android.pt.mutex);
  9420. }
  9421. pthread_mutex_unlock(&_sapp.android.pt.mutex);
  9422. }
  9423. _SOKOL_PRIVATE void _sapp_android_on_input_queue_created(ANativeActivity* activity, AInputQueue* queue) {
  9424. _SOKOL_UNUSED(activity);
  9425. _SAPP_INFO(ANDROID_NATIVE_ACTIVITY_ONINPUTQUEUECREATED);
  9426. _sapp_android_msg_set_input_queue(queue);
  9427. }
  9428. _SOKOL_PRIVATE void _sapp_android_on_input_queue_destroyed(ANativeActivity* activity, AInputQueue* queue) {
  9429. _SOKOL_UNUSED(activity);
  9430. _SOKOL_UNUSED(queue);
  9431. _SAPP_INFO(ANDROID_NATIVE_ACTIVITY_ONINPUTQUEUEDESTROYED);
  9432. _sapp_android_msg_set_input_queue(NULL);
  9433. }
  9434. _SOKOL_PRIVATE void _sapp_android_on_config_changed(ANativeActivity* activity) {
  9435. _SOKOL_UNUSED(activity);
  9436. _SAPP_INFO(ANDROID_NATIVE_ACTIVITY_ONCONFIGURATIONCHANGED);
  9437. /* see android:configChanges in manifest */
  9438. }
  9439. _SOKOL_PRIVATE void _sapp_android_on_low_memory(ANativeActivity* activity) {
  9440. _SOKOL_UNUSED(activity);
  9441. _SAPP_INFO(ANDROID_NATIVE_ACTIVITY_ONLOWMEMORY);
  9442. }
  9443. _SOKOL_PRIVATE void _sapp_android_on_destroy(ANativeActivity* activity) {
  9444. /*
  9445. * For some reason even an empty app using nativeactivity.h will crash (WIN DEATH)
  9446. * on my device (Moto X 2nd gen) when the app is removed from the task view
  9447. * (TaskStackView: onTaskViewDismissed).
  9448. *
  9449. * However, if ANativeActivity_finish() is explicitly called from for example
  9450. * _sapp_android_on_stop(), the crash disappears. Is this a bug in NativeActivity?
  9451. */
  9452. _SOKOL_UNUSED(activity);
  9453. _SAPP_INFO(ANDROID_NATIVE_ACTIVITY_ONDESTROY);
  9454. /* send destroy msg */
  9455. pthread_mutex_lock(&_sapp.android.pt.mutex);
  9456. _sapp_android_msg(_SOKOL_ANDROID_MSG_DESTROY);
  9457. while (!_sapp.android.is_thread_stopped) {
  9458. pthread_cond_wait(&_sapp.android.pt.cond, &_sapp.android.pt.mutex);
  9459. }
  9460. pthread_mutex_unlock(&_sapp.android.pt.mutex);
  9461. /* clean up main thread */
  9462. pthread_cond_destroy(&_sapp.android.pt.cond);
  9463. pthread_mutex_destroy(&_sapp.android.pt.mutex);
  9464. close(_sapp.android.pt.read_from_main_fd);
  9465. close(_sapp.android.pt.write_from_main_fd);
  9466. _SAPP_INFO(ANDROID_NATIVE_ACTIVITY_DONE);
  9467. /* this is a bit naughty, but causes a clean restart of the app (static globals are reset) */
  9468. exit(0);
  9469. }
  9470. JNIEXPORT
  9471. void ANativeActivity_onCreate(ANativeActivity* activity, void* saved_state, size_t saved_state_size) {
  9472. _SOKOL_UNUSED(saved_state);
  9473. _SOKOL_UNUSED(saved_state_size);
  9474. _SAPP_INFO(ANDROID_NATIVE_ACTIVITY_ONCREATE);
  9475. // the NativeActity pointer needs to be available inside sokol_main()
  9476. // (see https://github.com/floooh/sokol/issues/708), however _sapp_init_state()
  9477. // will clear the global _sapp_t struct, so we need to initialize the native
  9478. // activity pointer twice, once before sokol_main() and once after _sapp_init_state()
  9479. _sapp_clear(&_sapp, sizeof(_sapp));
  9480. _sapp.android.activity = activity;
  9481. sapp_desc desc = sokol_main(0, NULL);
  9482. _sapp_init_state(&desc);
  9483. _sapp.android.activity = activity;
  9484. int pipe_fd[2];
  9485. if (pipe(pipe_fd) != 0) {
  9486. _SAPP_ERROR(ANDROID_CREATE_THREAD_PIPE_FAILED);
  9487. return;
  9488. }
  9489. _sapp.android.pt.read_from_main_fd = pipe_fd[0];
  9490. _sapp.android.pt.write_from_main_fd = pipe_fd[1];
  9491. pthread_mutex_init(&_sapp.android.pt.mutex, NULL);
  9492. pthread_cond_init(&_sapp.android.pt.cond, NULL);
  9493. pthread_attr_t attr;
  9494. pthread_attr_init(&attr);
  9495. pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
  9496. pthread_create(&_sapp.android.pt.thread, &attr, _sapp_android_loop, 0);
  9497. pthread_attr_destroy(&attr);
  9498. /* wait until main loop has started */
  9499. pthread_mutex_lock(&_sapp.android.pt.mutex);
  9500. while (!_sapp.android.is_thread_started) {
  9501. pthread_cond_wait(&_sapp.android.pt.cond, &_sapp.android.pt.mutex);
  9502. }
  9503. pthread_mutex_unlock(&_sapp.android.pt.mutex);
  9504. /* send create msg */
  9505. pthread_mutex_lock(&_sapp.android.pt.mutex);
  9506. _sapp_android_msg(_SOKOL_ANDROID_MSG_CREATE);
  9507. while (!_sapp.android.has_created) {
  9508. pthread_cond_wait(&_sapp.android.pt.cond, &_sapp.android.pt.mutex);
  9509. }
  9510. pthread_mutex_unlock(&_sapp.android.pt.mutex);
  9511. /* register for callbacks */
  9512. activity->callbacks->onStart = _sapp_android_on_start;
  9513. activity->callbacks->onResume = _sapp_android_on_resume;
  9514. activity->callbacks->onSaveInstanceState = _sapp_android_on_save_instance_state;
  9515. activity->callbacks->onWindowFocusChanged = _sapp_android_on_window_focus_changed;
  9516. activity->callbacks->onPause = _sapp_android_on_pause;
  9517. activity->callbacks->onStop = _sapp_android_on_stop;
  9518. activity->callbacks->onDestroy = _sapp_android_on_destroy;
  9519. activity->callbacks->onNativeWindowCreated = _sapp_android_on_native_window_created;
  9520. /* activity->callbacks->onNativeWindowResized = _sapp_android_on_native_window_resized; */
  9521. /* activity->callbacks->onNativeWindowRedrawNeeded = _sapp_android_on_native_window_redraw_needed; */
  9522. activity->callbacks->onNativeWindowDestroyed = _sapp_android_on_native_window_destroyed;
  9523. activity->callbacks->onInputQueueCreated = _sapp_android_on_input_queue_created;
  9524. activity->callbacks->onInputQueueDestroyed = _sapp_android_on_input_queue_destroyed;
  9525. /* activity->callbacks->onContentRectChanged = _sapp_android_on_content_rect_changed; */
  9526. /* activity->callbacks->onConfigurationChanged = _sapp_android_on_config_changed; */
  9527. activity->callbacks->onLowMemory = _sapp_android_on_low_memory;
  9528. _SAPP_INFO(ANDROID_NATIVE_ACTIVITY_CREATE_SUCCESS);
  9529. /* NOT A BUG: do NOT call sapp_discard_state() */
  9530. }
  9531. #endif /* _SAPP_ANDROID */
  9532. // ██ ██ ███ ██ ██ ██ ██ ██
  9533. // ██ ██ ████ ██ ██ ██ ██ ██
  9534. // ██ ██ ██ ██ ██ ██ ██ ███
  9535. // ██ ██ ██ ██ ██ ██ ██ ██ ██
  9536. // ███████ ██ ██ ████ ██████ ██ ██
  9537. //
  9538. // >>linux
  9539. #if defined(_SAPP_LINUX)
  9540. /* see GLFW's xkb_unicode.c */
  9541. static const struct _sapp_x11_codepair {
  9542. uint16_t keysym;
  9543. uint16_t ucs;
  9544. } _sapp_x11_keysymtab[] = {
  9545. { 0x01a1, 0x0104 },
  9546. { 0x01a2, 0x02d8 },
  9547. { 0x01a3, 0x0141 },
  9548. { 0x01a5, 0x013d },
  9549. { 0x01a6, 0x015a },
  9550. { 0x01a9, 0x0160 },
  9551. { 0x01aa, 0x015e },
  9552. { 0x01ab, 0x0164 },
  9553. { 0x01ac, 0x0179 },
  9554. { 0x01ae, 0x017d },
  9555. { 0x01af, 0x017b },
  9556. { 0x01b1, 0x0105 },
  9557. { 0x01b2, 0x02db },
  9558. { 0x01b3, 0x0142 },
  9559. { 0x01b5, 0x013e },
  9560. { 0x01b6, 0x015b },
  9561. { 0x01b7, 0x02c7 },
  9562. { 0x01b9, 0x0161 },
  9563. { 0x01ba, 0x015f },
  9564. { 0x01bb, 0x0165 },
  9565. { 0x01bc, 0x017a },
  9566. { 0x01bd, 0x02dd },
  9567. { 0x01be, 0x017e },
  9568. { 0x01bf, 0x017c },
  9569. { 0x01c0, 0x0154 },
  9570. { 0x01c3, 0x0102 },
  9571. { 0x01c5, 0x0139 },
  9572. { 0x01c6, 0x0106 },
  9573. { 0x01c8, 0x010c },
  9574. { 0x01ca, 0x0118 },
  9575. { 0x01cc, 0x011a },
  9576. { 0x01cf, 0x010e },
  9577. { 0x01d0, 0x0110 },
  9578. { 0x01d1, 0x0143 },
  9579. { 0x01d2, 0x0147 },
  9580. { 0x01d5, 0x0150 },
  9581. { 0x01d8, 0x0158 },
  9582. { 0x01d9, 0x016e },
  9583. { 0x01db, 0x0170 },
  9584. { 0x01de, 0x0162 },
  9585. { 0x01e0, 0x0155 },
  9586. { 0x01e3, 0x0103 },
  9587. { 0x01e5, 0x013a },
  9588. { 0x01e6, 0x0107 },
  9589. { 0x01e8, 0x010d },
  9590. { 0x01ea, 0x0119 },
  9591. { 0x01ec, 0x011b },
  9592. { 0x01ef, 0x010f },
  9593. { 0x01f0, 0x0111 },
  9594. { 0x01f1, 0x0144 },
  9595. { 0x01f2, 0x0148 },
  9596. { 0x01f5, 0x0151 },
  9597. { 0x01f8, 0x0159 },
  9598. { 0x01f9, 0x016f },
  9599. { 0x01fb, 0x0171 },
  9600. { 0x01fe, 0x0163 },
  9601. { 0x01ff, 0x02d9 },
  9602. { 0x02a1, 0x0126 },
  9603. { 0x02a6, 0x0124 },
  9604. { 0x02a9, 0x0130 },
  9605. { 0x02ab, 0x011e },
  9606. { 0x02ac, 0x0134 },
  9607. { 0x02b1, 0x0127 },
  9608. { 0x02b6, 0x0125 },
  9609. { 0x02b9, 0x0131 },
  9610. { 0x02bb, 0x011f },
  9611. { 0x02bc, 0x0135 },
  9612. { 0x02c5, 0x010a },
  9613. { 0x02c6, 0x0108 },
  9614. { 0x02d5, 0x0120 },
  9615. { 0x02d8, 0x011c },
  9616. { 0x02dd, 0x016c },
  9617. { 0x02de, 0x015c },
  9618. { 0x02e5, 0x010b },
  9619. { 0x02e6, 0x0109 },
  9620. { 0x02f5, 0x0121 },
  9621. { 0x02f8, 0x011d },
  9622. { 0x02fd, 0x016d },
  9623. { 0x02fe, 0x015d },
  9624. { 0x03a2, 0x0138 },
  9625. { 0x03a3, 0x0156 },
  9626. { 0x03a5, 0x0128 },
  9627. { 0x03a6, 0x013b },
  9628. { 0x03aa, 0x0112 },
  9629. { 0x03ab, 0x0122 },
  9630. { 0x03ac, 0x0166 },
  9631. { 0x03b3, 0x0157 },
  9632. { 0x03b5, 0x0129 },
  9633. { 0x03b6, 0x013c },
  9634. { 0x03ba, 0x0113 },
  9635. { 0x03bb, 0x0123 },
  9636. { 0x03bc, 0x0167 },
  9637. { 0x03bd, 0x014a },
  9638. { 0x03bf, 0x014b },
  9639. { 0x03c0, 0x0100 },
  9640. { 0x03c7, 0x012e },
  9641. { 0x03cc, 0x0116 },
  9642. { 0x03cf, 0x012a },
  9643. { 0x03d1, 0x0145 },
  9644. { 0x03d2, 0x014c },
  9645. { 0x03d3, 0x0136 },
  9646. { 0x03d9, 0x0172 },
  9647. { 0x03dd, 0x0168 },
  9648. { 0x03de, 0x016a },
  9649. { 0x03e0, 0x0101 },
  9650. { 0x03e7, 0x012f },
  9651. { 0x03ec, 0x0117 },
  9652. { 0x03ef, 0x012b },
  9653. { 0x03f1, 0x0146 },
  9654. { 0x03f2, 0x014d },
  9655. { 0x03f3, 0x0137 },
  9656. { 0x03f9, 0x0173 },
  9657. { 0x03fd, 0x0169 },
  9658. { 0x03fe, 0x016b },
  9659. { 0x047e, 0x203e },
  9660. { 0x04a1, 0x3002 },
  9661. { 0x04a2, 0x300c },
  9662. { 0x04a3, 0x300d },
  9663. { 0x04a4, 0x3001 },
  9664. { 0x04a5, 0x30fb },
  9665. { 0x04a6, 0x30f2 },
  9666. { 0x04a7, 0x30a1 },
  9667. { 0x04a8, 0x30a3 },
  9668. { 0x04a9, 0x30a5 },
  9669. { 0x04aa, 0x30a7 },
  9670. { 0x04ab, 0x30a9 },
  9671. { 0x04ac, 0x30e3 },
  9672. { 0x04ad, 0x30e5 },
  9673. { 0x04ae, 0x30e7 },
  9674. { 0x04af, 0x30c3 },
  9675. { 0x04b0, 0x30fc },
  9676. { 0x04b1, 0x30a2 },
  9677. { 0x04b2, 0x30a4 },
  9678. { 0x04b3, 0x30a6 },
  9679. { 0x04b4, 0x30a8 },
  9680. { 0x04b5, 0x30aa },
  9681. { 0x04b6, 0x30ab },
  9682. { 0x04b7, 0x30ad },
  9683. { 0x04b8, 0x30af },
  9684. { 0x04b9, 0x30b1 },
  9685. { 0x04ba, 0x30b3 },
  9686. { 0x04bb, 0x30b5 },
  9687. { 0x04bc, 0x30b7 },
  9688. { 0x04bd, 0x30b9 },
  9689. { 0x04be, 0x30bb },
  9690. { 0x04bf, 0x30bd },
  9691. { 0x04c0, 0x30bf },
  9692. { 0x04c1, 0x30c1 },
  9693. { 0x04c2, 0x30c4 },
  9694. { 0x04c3, 0x30c6 },
  9695. { 0x04c4, 0x30c8 },
  9696. { 0x04c5, 0x30ca },
  9697. { 0x04c6, 0x30cb },
  9698. { 0x04c7, 0x30cc },
  9699. { 0x04c8, 0x30cd },
  9700. { 0x04c9, 0x30ce },
  9701. { 0x04ca, 0x30cf },
  9702. { 0x04cb, 0x30d2 },
  9703. { 0x04cc, 0x30d5 },
  9704. { 0x04cd, 0x30d8 },
  9705. { 0x04ce, 0x30db },
  9706. { 0x04cf, 0x30de },
  9707. { 0x04d0, 0x30df },
  9708. { 0x04d1, 0x30e0 },
  9709. { 0x04d2, 0x30e1 },
  9710. { 0x04d3, 0x30e2 },
  9711. { 0x04d4, 0x30e4 },
  9712. { 0x04d5, 0x30e6 },
  9713. { 0x04d6, 0x30e8 },
  9714. { 0x04d7, 0x30e9 },
  9715. { 0x04d8, 0x30ea },
  9716. { 0x04d9, 0x30eb },
  9717. { 0x04da, 0x30ec },
  9718. { 0x04db, 0x30ed },
  9719. { 0x04dc, 0x30ef },
  9720. { 0x04dd, 0x30f3 },
  9721. { 0x04de, 0x309b },
  9722. { 0x04df, 0x309c },
  9723. { 0x05ac, 0x060c },
  9724. { 0x05bb, 0x061b },
  9725. { 0x05bf, 0x061f },
  9726. { 0x05c1, 0x0621 },
  9727. { 0x05c2, 0x0622 },
  9728. { 0x05c3, 0x0623 },
  9729. { 0x05c4, 0x0624 },
  9730. { 0x05c5, 0x0625 },
  9731. { 0x05c6, 0x0626 },
  9732. { 0x05c7, 0x0627 },
  9733. { 0x05c8, 0x0628 },
  9734. { 0x05c9, 0x0629 },
  9735. { 0x05ca, 0x062a },
  9736. { 0x05cb, 0x062b },
  9737. { 0x05cc, 0x062c },
  9738. { 0x05cd, 0x062d },
  9739. { 0x05ce, 0x062e },
  9740. { 0x05cf, 0x062f },
  9741. { 0x05d0, 0x0630 },
  9742. { 0x05d1, 0x0631 },
  9743. { 0x05d2, 0x0632 },
  9744. { 0x05d3, 0x0633 },
  9745. { 0x05d4, 0x0634 },
  9746. { 0x05d5, 0x0635 },
  9747. { 0x05d6, 0x0636 },
  9748. { 0x05d7, 0x0637 },
  9749. { 0x05d8, 0x0638 },
  9750. { 0x05d9, 0x0639 },
  9751. { 0x05da, 0x063a },
  9752. { 0x05e0, 0x0640 },
  9753. { 0x05e1, 0x0641 },
  9754. { 0x05e2, 0x0642 },
  9755. { 0x05e3, 0x0643 },
  9756. { 0x05e4, 0x0644 },
  9757. { 0x05e5, 0x0645 },
  9758. { 0x05e6, 0x0646 },
  9759. { 0x05e7, 0x0647 },
  9760. { 0x05e8, 0x0648 },
  9761. { 0x05e9, 0x0649 },
  9762. { 0x05ea, 0x064a },
  9763. { 0x05eb, 0x064b },
  9764. { 0x05ec, 0x064c },
  9765. { 0x05ed, 0x064d },
  9766. { 0x05ee, 0x064e },
  9767. { 0x05ef, 0x064f },
  9768. { 0x05f0, 0x0650 },
  9769. { 0x05f1, 0x0651 },
  9770. { 0x05f2, 0x0652 },
  9771. { 0x06a1, 0x0452 },
  9772. { 0x06a2, 0x0453 },
  9773. { 0x06a3, 0x0451 },
  9774. { 0x06a4, 0x0454 },
  9775. { 0x06a5, 0x0455 },
  9776. { 0x06a6, 0x0456 },
  9777. { 0x06a7, 0x0457 },
  9778. { 0x06a8, 0x0458 },
  9779. { 0x06a9, 0x0459 },
  9780. { 0x06aa, 0x045a },
  9781. { 0x06ab, 0x045b },
  9782. { 0x06ac, 0x045c },
  9783. { 0x06ae, 0x045e },
  9784. { 0x06af, 0x045f },
  9785. { 0x06b0, 0x2116 },
  9786. { 0x06b1, 0x0402 },
  9787. { 0x06b2, 0x0403 },
  9788. { 0x06b3, 0x0401 },
  9789. { 0x06b4, 0x0404 },
  9790. { 0x06b5, 0x0405 },
  9791. { 0x06b6, 0x0406 },
  9792. { 0x06b7, 0x0407 },
  9793. { 0x06b8, 0x0408 },
  9794. { 0x06b9, 0x0409 },
  9795. { 0x06ba, 0x040a },
  9796. { 0x06bb, 0x040b },
  9797. { 0x06bc, 0x040c },
  9798. { 0x06be, 0x040e },
  9799. { 0x06bf, 0x040f },
  9800. { 0x06c0, 0x044e },
  9801. { 0x06c1, 0x0430 },
  9802. { 0x06c2, 0x0431 },
  9803. { 0x06c3, 0x0446 },
  9804. { 0x06c4, 0x0434 },
  9805. { 0x06c5, 0x0435 },
  9806. { 0x06c6, 0x0444 },
  9807. { 0x06c7, 0x0433 },
  9808. { 0x06c8, 0x0445 },
  9809. { 0x06c9, 0x0438 },
  9810. { 0x06ca, 0x0439 },
  9811. { 0x06cb, 0x043a },
  9812. { 0x06cc, 0x043b },
  9813. { 0x06cd, 0x043c },
  9814. { 0x06ce, 0x043d },
  9815. { 0x06cf, 0x043e },
  9816. { 0x06d0, 0x043f },
  9817. { 0x06d1, 0x044f },
  9818. { 0x06d2, 0x0440 },
  9819. { 0x06d3, 0x0441 },
  9820. { 0x06d4, 0x0442 },
  9821. { 0x06d5, 0x0443 },
  9822. { 0x06d6, 0x0436 },
  9823. { 0x06d7, 0x0432 },
  9824. { 0x06d8, 0x044c },
  9825. { 0x06d9, 0x044b },
  9826. { 0x06da, 0x0437 },
  9827. { 0x06db, 0x0448 },
  9828. { 0x06dc, 0x044d },
  9829. { 0x06dd, 0x0449 },
  9830. { 0x06de, 0x0447 },
  9831. { 0x06df, 0x044a },
  9832. { 0x06e0, 0x042e },
  9833. { 0x06e1, 0x0410 },
  9834. { 0x06e2, 0x0411 },
  9835. { 0x06e3, 0x0426 },
  9836. { 0x06e4, 0x0414 },
  9837. { 0x06e5, 0x0415 },
  9838. { 0x06e6, 0x0424 },
  9839. { 0x06e7, 0x0413 },
  9840. { 0x06e8, 0x0425 },
  9841. { 0x06e9, 0x0418 },
  9842. { 0x06ea, 0x0419 },
  9843. { 0x06eb, 0x041a },
  9844. { 0x06ec, 0x041b },
  9845. { 0x06ed, 0x041c },
  9846. { 0x06ee, 0x041d },
  9847. { 0x06ef, 0x041e },
  9848. { 0x06f0, 0x041f },
  9849. { 0x06f1, 0x042f },
  9850. { 0x06f2, 0x0420 },
  9851. { 0x06f3, 0x0421 },
  9852. { 0x06f4, 0x0422 },
  9853. { 0x06f5, 0x0423 },
  9854. { 0x06f6, 0x0416 },
  9855. { 0x06f7, 0x0412 },
  9856. { 0x06f8, 0x042c },
  9857. { 0x06f9, 0x042b },
  9858. { 0x06fa, 0x0417 },
  9859. { 0x06fb, 0x0428 },
  9860. { 0x06fc, 0x042d },
  9861. { 0x06fd, 0x0429 },
  9862. { 0x06fe, 0x0427 },
  9863. { 0x06ff, 0x042a },
  9864. { 0x07a1, 0x0386 },
  9865. { 0x07a2, 0x0388 },
  9866. { 0x07a3, 0x0389 },
  9867. { 0x07a4, 0x038a },
  9868. { 0x07a5, 0x03aa },
  9869. { 0x07a7, 0x038c },
  9870. { 0x07a8, 0x038e },
  9871. { 0x07a9, 0x03ab },
  9872. { 0x07ab, 0x038f },
  9873. { 0x07ae, 0x0385 },
  9874. { 0x07af, 0x2015 },
  9875. { 0x07b1, 0x03ac },
  9876. { 0x07b2, 0x03ad },
  9877. { 0x07b3, 0x03ae },
  9878. { 0x07b4, 0x03af },
  9879. { 0x07b5, 0x03ca },
  9880. { 0x07b6, 0x0390 },
  9881. { 0x07b7, 0x03cc },
  9882. { 0x07b8, 0x03cd },
  9883. { 0x07b9, 0x03cb },
  9884. { 0x07ba, 0x03b0 },
  9885. { 0x07bb, 0x03ce },
  9886. { 0x07c1, 0x0391 },
  9887. { 0x07c2, 0x0392 },
  9888. { 0x07c3, 0x0393 },
  9889. { 0x07c4, 0x0394 },
  9890. { 0x07c5, 0x0395 },
  9891. { 0x07c6, 0x0396 },
  9892. { 0x07c7, 0x0397 },
  9893. { 0x07c8, 0x0398 },
  9894. { 0x07c9, 0x0399 },
  9895. { 0x07ca, 0x039a },
  9896. { 0x07cb, 0x039b },
  9897. { 0x07cc, 0x039c },
  9898. { 0x07cd, 0x039d },
  9899. { 0x07ce, 0x039e },
  9900. { 0x07cf, 0x039f },
  9901. { 0x07d0, 0x03a0 },
  9902. { 0x07d1, 0x03a1 },
  9903. { 0x07d2, 0x03a3 },
  9904. { 0x07d4, 0x03a4 },
  9905. { 0x07d5, 0x03a5 },
  9906. { 0x07d6, 0x03a6 },
  9907. { 0x07d7, 0x03a7 },
  9908. { 0x07d8, 0x03a8 },
  9909. { 0x07d9, 0x03a9 },
  9910. { 0x07e1, 0x03b1 },
  9911. { 0x07e2, 0x03b2 },
  9912. { 0x07e3, 0x03b3 },
  9913. { 0x07e4, 0x03b4 },
  9914. { 0x07e5, 0x03b5 },
  9915. { 0x07e6, 0x03b6 },
  9916. { 0x07e7, 0x03b7 },
  9917. { 0x07e8, 0x03b8 },
  9918. { 0x07e9, 0x03b9 },
  9919. { 0x07ea, 0x03ba },
  9920. { 0x07eb, 0x03bb },
  9921. { 0x07ec, 0x03bc },
  9922. { 0x07ed, 0x03bd },
  9923. { 0x07ee, 0x03be },
  9924. { 0x07ef, 0x03bf },
  9925. { 0x07f0, 0x03c0 },
  9926. { 0x07f1, 0x03c1 },
  9927. { 0x07f2, 0x03c3 },
  9928. { 0x07f3, 0x03c2 },
  9929. { 0x07f4, 0x03c4 },
  9930. { 0x07f5, 0x03c5 },
  9931. { 0x07f6, 0x03c6 },
  9932. { 0x07f7, 0x03c7 },
  9933. { 0x07f8, 0x03c8 },
  9934. { 0x07f9, 0x03c9 },
  9935. { 0x08a1, 0x23b7 },
  9936. { 0x08a2, 0x250c },
  9937. { 0x08a3, 0x2500 },
  9938. { 0x08a4, 0x2320 },
  9939. { 0x08a5, 0x2321 },
  9940. { 0x08a6, 0x2502 },
  9941. { 0x08a7, 0x23a1 },
  9942. { 0x08a8, 0x23a3 },
  9943. { 0x08a9, 0x23a4 },
  9944. { 0x08aa, 0x23a6 },
  9945. { 0x08ab, 0x239b },
  9946. { 0x08ac, 0x239d },
  9947. { 0x08ad, 0x239e },
  9948. { 0x08ae, 0x23a0 },
  9949. { 0x08af, 0x23a8 },
  9950. { 0x08b0, 0x23ac },
  9951. { 0x08bc, 0x2264 },
  9952. { 0x08bd, 0x2260 },
  9953. { 0x08be, 0x2265 },
  9954. { 0x08bf, 0x222b },
  9955. { 0x08c0, 0x2234 },
  9956. { 0x08c1, 0x221d },
  9957. { 0x08c2, 0x221e },
  9958. { 0x08c5, 0x2207 },
  9959. { 0x08c8, 0x223c },
  9960. { 0x08c9, 0x2243 },
  9961. { 0x08cd, 0x21d4 },
  9962. { 0x08ce, 0x21d2 },
  9963. { 0x08cf, 0x2261 },
  9964. { 0x08d6, 0x221a },
  9965. { 0x08da, 0x2282 },
  9966. { 0x08db, 0x2283 },
  9967. { 0x08dc, 0x2229 },
  9968. { 0x08dd, 0x222a },
  9969. { 0x08de, 0x2227 },
  9970. { 0x08df, 0x2228 },
  9971. { 0x08ef, 0x2202 },
  9972. { 0x08f6, 0x0192 },
  9973. { 0x08fb, 0x2190 },
  9974. { 0x08fc, 0x2191 },
  9975. { 0x08fd, 0x2192 },
  9976. { 0x08fe, 0x2193 },
  9977. { 0x09e0, 0x25c6 },
  9978. { 0x09e1, 0x2592 },
  9979. { 0x09e2, 0x2409 },
  9980. { 0x09e3, 0x240c },
  9981. { 0x09e4, 0x240d },
  9982. { 0x09e5, 0x240a },
  9983. { 0x09e8, 0x2424 },
  9984. { 0x09e9, 0x240b },
  9985. { 0x09ea, 0x2518 },
  9986. { 0x09eb, 0x2510 },
  9987. { 0x09ec, 0x250c },
  9988. { 0x09ed, 0x2514 },
  9989. { 0x09ee, 0x253c },
  9990. { 0x09ef, 0x23ba },
  9991. { 0x09f0, 0x23bb },
  9992. { 0x09f1, 0x2500 },
  9993. { 0x09f2, 0x23bc },
  9994. { 0x09f3, 0x23bd },
  9995. { 0x09f4, 0x251c },
  9996. { 0x09f5, 0x2524 },
  9997. { 0x09f6, 0x2534 },
  9998. { 0x09f7, 0x252c },
  9999. { 0x09f8, 0x2502 },
  10000. { 0x0aa1, 0x2003 },
  10001. { 0x0aa2, 0x2002 },
  10002. { 0x0aa3, 0x2004 },
  10003. { 0x0aa4, 0x2005 },
  10004. { 0x0aa5, 0x2007 },
  10005. { 0x0aa6, 0x2008 },
  10006. { 0x0aa7, 0x2009 },
  10007. { 0x0aa8, 0x200a },
  10008. { 0x0aa9, 0x2014 },
  10009. { 0x0aaa, 0x2013 },
  10010. { 0x0aae, 0x2026 },
  10011. { 0x0aaf, 0x2025 },
  10012. { 0x0ab0, 0x2153 },
  10013. { 0x0ab1, 0x2154 },
  10014. { 0x0ab2, 0x2155 },
  10015. { 0x0ab3, 0x2156 },
  10016. { 0x0ab4, 0x2157 },
  10017. { 0x0ab5, 0x2158 },
  10018. { 0x0ab6, 0x2159 },
  10019. { 0x0ab7, 0x215a },
  10020. { 0x0ab8, 0x2105 },
  10021. { 0x0abb, 0x2012 },
  10022. { 0x0abc, 0x2329 },
  10023. { 0x0abe, 0x232a },
  10024. { 0x0ac3, 0x215b },
  10025. { 0x0ac4, 0x215c },
  10026. { 0x0ac5, 0x215d },
  10027. { 0x0ac6, 0x215e },
  10028. { 0x0ac9, 0x2122 },
  10029. { 0x0aca, 0x2613 },
  10030. { 0x0acc, 0x25c1 },
  10031. { 0x0acd, 0x25b7 },
  10032. { 0x0ace, 0x25cb },
  10033. { 0x0acf, 0x25af },
  10034. { 0x0ad0, 0x2018 },
  10035. { 0x0ad1, 0x2019 },
  10036. { 0x0ad2, 0x201c },
  10037. { 0x0ad3, 0x201d },
  10038. { 0x0ad4, 0x211e },
  10039. { 0x0ad6, 0x2032 },
  10040. { 0x0ad7, 0x2033 },
  10041. { 0x0ad9, 0x271d },
  10042. { 0x0adb, 0x25ac },
  10043. { 0x0adc, 0x25c0 },
  10044. { 0x0add, 0x25b6 },
  10045. { 0x0ade, 0x25cf },
  10046. { 0x0adf, 0x25ae },
  10047. { 0x0ae0, 0x25e6 },
  10048. { 0x0ae1, 0x25ab },
  10049. { 0x0ae2, 0x25ad },
  10050. { 0x0ae3, 0x25b3 },
  10051. { 0x0ae4, 0x25bd },
  10052. { 0x0ae5, 0x2606 },
  10053. { 0x0ae6, 0x2022 },
  10054. { 0x0ae7, 0x25aa },
  10055. { 0x0ae8, 0x25b2 },
  10056. { 0x0ae9, 0x25bc },
  10057. { 0x0aea, 0x261c },
  10058. { 0x0aeb, 0x261e },
  10059. { 0x0aec, 0x2663 },
  10060. { 0x0aed, 0x2666 },
  10061. { 0x0aee, 0x2665 },
  10062. { 0x0af0, 0x2720 },
  10063. { 0x0af1, 0x2020 },
  10064. { 0x0af2, 0x2021 },
  10065. { 0x0af3, 0x2713 },
  10066. { 0x0af4, 0x2717 },
  10067. { 0x0af5, 0x266f },
  10068. { 0x0af6, 0x266d },
  10069. { 0x0af7, 0x2642 },
  10070. { 0x0af8, 0x2640 },
  10071. { 0x0af9, 0x260e },
  10072. { 0x0afa, 0x2315 },
  10073. { 0x0afb, 0x2117 },
  10074. { 0x0afc, 0x2038 },
  10075. { 0x0afd, 0x201a },
  10076. { 0x0afe, 0x201e },
  10077. { 0x0ba3, 0x003c },
  10078. { 0x0ba6, 0x003e },
  10079. { 0x0ba8, 0x2228 },
  10080. { 0x0ba9, 0x2227 },
  10081. { 0x0bc0, 0x00af },
  10082. { 0x0bc2, 0x22a5 },
  10083. { 0x0bc3, 0x2229 },
  10084. { 0x0bc4, 0x230a },
  10085. { 0x0bc6, 0x005f },
  10086. { 0x0bca, 0x2218 },
  10087. { 0x0bcc, 0x2395 },
  10088. { 0x0bce, 0x22a4 },
  10089. { 0x0bcf, 0x25cb },
  10090. { 0x0bd3, 0x2308 },
  10091. { 0x0bd6, 0x222a },
  10092. { 0x0bd8, 0x2283 },
  10093. { 0x0bda, 0x2282 },
  10094. { 0x0bdc, 0x22a2 },
  10095. { 0x0bfc, 0x22a3 },
  10096. { 0x0cdf, 0x2017 },
  10097. { 0x0ce0, 0x05d0 },
  10098. { 0x0ce1, 0x05d1 },
  10099. { 0x0ce2, 0x05d2 },
  10100. { 0x0ce3, 0x05d3 },
  10101. { 0x0ce4, 0x05d4 },
  10102. { 0x0ce5, 0x05d5 },
  10103. { 0x0ce6, 0x05d6 },
  10104. { 0x0ce7, 0x05d7 },
  10105. { 0x0ce8, 0x05d8 },
  10106. { 0x0ce9, 0x05d9 },
  10107. { 0x0cea, 0x05da },
  10108. { 0x0ceb, 0x05db },
  10109. { 0x0cec, 0x05dc },
  10110. { 0x0ced, 0x05dd },
  10111. { 0x0cee, 0x05de },
  10112. { 0x0cef, 0x05df },
  10113. { 0x0cf0, 0x05e0 },
  10114. { 0x0cf1, 0x05e1 },
  10115. { 0x0cf2, 0x05e2 },
  10116. { 0x0cf3, 0x05e3 },
  10117. { 0x0cf4, 0x05e4 },
  10118. { 0x0cf5, 0x05e5 },
  10119. { 0x0cf6, 0x05e6 },
  10120. { 0x0cf7, 0x05e7 },
  10121. { 0x0cf8, 0x05e8 },
  10122. { 0x0cf9, 0x05e9 },
  10123. { 0x0cfa, 0x05ea },
  10124. { 0x0da1, 0x0e01 },
  10125. { 0x0da2, 0x0e02 },
  10126. { 0x0da3, 0x0e03 },
  10127. { 0x0da4, 0x0e04 },
  10128. { 0x0da5, 0x0e05 },
  10129. { 0x0da6, 0x0e06 },
  10130. { 0x0da7, 0x0e07 },
  10131. { 0x0da8, 0x0e08 },
  10132. { 0x0da9, 0x0e09 },
  10133. { 0x0daa, 0x0e0a },
  10134. { 0x0dab, 0x0e0b },
  10135. { 0x0dac, 0x0e0c },
  10136. { 0x0dad, 0x0e0d },
  10137. { 0x0dae, 0x0e0e },
  10138. { 0x0daf, 0x0e0f },
  10139. { 0x0db0, 0x0e10 },
  10140. { 0x0db1, 0x0e11 },
  10141. { 0x0db2, 0x0e12 },
  10142. { 0x0db3, 0x0e13 },
  10143. { 0x0db4, 0x0e14 },
  10144. { 0x0db5, 0x0e15 },
  10145. { 0x0db6, 0x0e16 },
  10146. { 0x0db7, 0x0e17 },
  10147. { 0x0db8, 0x0e18 },
  10148. { 0x0db9, 0x0e19 },
  10149. { 0x0dba, 0x0e1a },
  10150. { 0x0dbb, 0x0e1b },
  10151. { 0x0dbc, 0x0e1c },
  10152. { 0x0dbd, 0x0e1d },
  10153. { 0x0dbe, 0x0e1e },
  10154. { 0x0dbf, 0x0e1f },
  10155. { 0x0dc0, 0x0e20 },
  10156. { 0x0dc1, 0x0e21 },
  10157. { 0x0dc2, 0x0e22 },
  10158. { 0x0dc3, 0x0e23 },
  10159. { 0x0dc4, 0x0e24 },
  10160. { 0x0dc5, 0x0e25 },
  10161. { 0x0dc6, 0x0e26 },
  10162. { 0x0dc7, 0x0e27 },
  10163. { 0x0dc8, 0x0e28 },
  10164. { 0x0dc9, 0x0e29 },
  10165. { 0x0dca, 0x0e2a },
  10166. { 0x0dcb, 0x0e2b },
  10167. { 0x0dcc, 0x0e2c },
  10168. { 0x0dcd, 0x0e2d },
  10169. { 0x0dce, 0x0e2e },
  10170. { 0x0dcf, 0x0e2f },
  10171. { 0x0dd0, 0x0e30 },
  10172. { 0x0dd1, 0x0e31 },
  10173. { 0x0dd2, 0x0e32 },
  10174. { 0x0dd3, 0x0e33 },
  10175. { 0x0dd4, 0x0e34 },
  10176. { 0x0dd5, 0x0e35 },
  10177. { 0x0dd6, 0x0e36 },
  10178. { 0x0dd7, 0x0e37 },
  10179. { 0x0dd8, 0x0e38 },
  10180. { 0x0dd9, 0x0e39 },
  10181. { 0x0dda, 0x0e3a },
  10182. { 0x0ddf, 0x0e3f },
  10183. { 0x0de0, 0x0e40 },
  10184. { 0x0de1, 0x0e41 },
  10185. { 0x0de2, 0x0e42 },
  10186. { 0x0de3, 0x0e43 },
  10187. { 0x0de4, 0x0e44 },
  10188. { 0x0de5, 0x0e45 },
  10189. { 0x0de6, 0x0e46 },
  10190. { 0x0de7, 0x0e47 },
  10191. { 0x0de8, 0x0e48 },
  10192. { 0x0de9, 0x0e49 },
  10193. { 0x0dea, 0x0e4a },
  10194. { 0x0deb, 0x0e4b },
  10195. { 0x0dec, 0x0e4c },
  10196. { 0x0ded, 0x0e4d },
  10197. { 0x0df0, 0x0e50 },
  10198. { 0x0df1, 0x0e51 },
  10199. { 0x0df2, 0x0e52 },
  10200. { 0x0df3, 0x0e53 },
  10201. { 0x0df4, 0x0e54 },
  10202. { 0x0df5, 0x0e55 },
  10203. { 0x0df6, 0x0e56 },
  10204. { 0x0df7, 0x0e57 },
  10205. { 0x0df8, 0x0e58 },
  10206. { 0x0df9, 0x0e59 },
  10207. { 0x0ea1, 0x3131 },
  10208. { 0x0ea2, 0x3132 },
  10209. { 0x0ea3, 0x3133 },
  10210. { 0x0ea4, 0x3134 },
  10211. { 0x0ea5, 0x3135 },
  10212. { 0x0ea6, 0x3136 },
  10213. { 0x0ea7, 0x3137 },
  10214. { 0x0ea8, 0x3138 },
  10215. { 0x0ea9, 0x3139 },
  10216. { 0x0eaa, 0x313a },
  10217. { 0x0eab, 0x313b },
  10218. { 0x0eac, 0x313c },
  10219. { 0x0ead, 0x313d },
  10220. { 0x0eae, 0x313e },
  10221. { 0x0eaf, 0x313f },
  10222. { 0x0eb0, 0x3140 },
  10223. { 0x0eb1, 0x3141 },
  10224. { 0x0eb2, 0x3142 },
  10225. { 0x0eb3, 0x3143 },
  10226. { 0x0eb4, 0x3144 },
  10227. { 0x0eb5, 0x3145 },
  10228. { 0x0eb6, 0x3146 },
  10229. { 0x0eb7, 0x3147 },
  10230. { 0x0eb8, 0x3148 },
  10231. { 0x0eb9, 0x3149 },
  10232. { 0x0eba, 0x314a },
  10233. { 0x0ebb, 0x314b },
  10234. { 0x0ebc, 0x314c },
  10235. { 0x0ebd, 0x314d },
  10236. { 0x0ebe, 0x314e },
  10237. { 0x0ebf, 0x314f },
  10238. { 0x0ec0, 0x3150 },
  10239. { 0x0ec1, 0x3151 },
  10240. { 0x0ec2, 0x3152 },
  10241. { 0x0ec3, 0x3153 },
  10242. { 0x0ec4, 0x3154 },
  10243. { 0x0ec5, 0x3155 },
  10244. { 0x0ec6, 0x3156 },
  10245. { 0x0ec7, 0x3157 },
  10246. { 0x0ec8, 0x3158 },
  10247. { 0x0ec9, 0x3159 },
  10248. { 0x0eca, 0x315a },
  10249. { 0x0ecb, 0x315b },
  10250. { 0x0ecc, 0x315c },
  10251. { 0x0ecd, 0x315d },
  10252. { 0x0ece, 0x315e },
  10253. { 0x0ecf, 0x315f },
  10254. { 0x0ed0, 0x3160 },
  10255. { 0x0ed1, 0x3161 },
  10256. { 0x0ed2, 0x3162 },
  10257. { 0x0ed3, 0x3163 },
  10258. { 0x0ed4, 0x11a8 },
  10259. { 0x0ed5, 0x11a9 },
  10260. { 0x0ed6, 0x11aa },
  10261. { 0x0ed7, 0x11ab },
  10262. { 0x0ed8, 0x11ac },
  10263. { 0x0ed9, 0x11ad },
  10264. { 0x0eda, 0x11ae },
  10265. { 0x0edb, 0x11af },
  10266. { 0x0edc, 0x11b0 },
  10267. { 0x0edd, 0x11b1 },
  10268. { 0x0ede, 0x11b2 },
  10269. { 0x0edf, 0x11b3 },
  10270. { 0x0ee0, 0x11b4 },
  10271. { 0x0ee1, 0x11b5 },
  10272. { 0x0ee2, 0x11b6 },
  10273. { 0x0ee3, 0x11b7 },
  10274. { 0x0ee4, 0x11b8 },
  10275. { 0x0ee5, 0x11b9 },
  10276. { 0x0ee6, 0x11ba },
  10277. { 0x0ee7, 0x11bb },
  10278. { 0x0ee8, 0x11bc },
  10279. { 0x0ee9, 0x11bd },
  10280. { 0x0eea, 0x11be },
  10281. { 0x0eeb, 0x11bf },
  10282. { 0x0eec, 0x11c0 },
  10283. { 0x0eed, 0x11c1 },
  10284. { 0x0eee, 0x11c2 },
  10285. { 0x0eef, 0x316d },
  10286. { 0x0ef0, 0x3171 },
  10287. { 0x0ef1, 0x3178 },
  10288. { 0x0ef2, 0x317f },
  10289. { 0x0ef3, 0x3181 },
  10290. { 0x0ef4, 0x3184 },
  10291. { 0x0ef5, 0x3186 },
  10292. { 0x0ef6, 0x318d },
  10293. { 0x0ef7, 0x318e },
  10294. { 0x0ef8, 0x11eb },
  10295. { 0x0ef9, 0x11f0 },
  10296. { 0x0efa, 0x11f9 },
  10297. { 0x0eff, 0x20a9 },
  10298. { 0x13a4, 0x20ac },
  10299. { 0x13bc, 0x0152 },
  10300. { 0x13bd, 0x0153 },
  10301. { 0x13be, 0x0178 },
  10302. { 0x20ac, 0x20ac },
  10303. { 0xfe50, '`' },
  10304. { 0xfe51, 0x00b4 },
  10305. { 0xfe52, '^' },
  10306. { 0xfe53, '~' },
  10307. { 0xfe54, 0x00af },
  10308. { 0xfe55, 0x02d8 },
  10309. { 0xfe56, 0x02d9 },
  10310. { 0xfe57, 0x00a8 },
  10311. { 0xfe58, 0x02da },
  10312. { 0xfe59, 0x02dd },
  10313. { 0xfe5a, 0x02c7 },
  10314. { 0xfe5b, 0x00b8 },
  10315. { 0xfe5c, 0x02db },
  10316. { 0xfe5d, 0x037a },
  10317. { 0xfe5e, 0x309b },
  10318. { 0xfe5f, 0x309c },
  10319. { 0xfe63, '/' },
  10320. { 0xfe64, 0x02bc },
  10321. { 0xfe65, 0x02bd },
  10322. { 0xfe66, 0x02f5 },
  10323. { 0xfe67, 0x02f3 },
  10324. { 0xfe68, 0x02cd },
  10325. { 0xfe69, 0xa788 },
  10326. { 0xfe6a, 0x02f7 },
  10327. { 0xfe6e, ',' },
  10328. { 0xfe6f, 0x00a4 },
  10329. { 0xfe80, 'a' }, /* XK_dead_a */
  10330. { 0xfe81, 'A' }, /* XK_dead_A */
  10331. { 0xfe82, 'e' }, /* XK_dead_e */
  10332. { 0xfe83, 'E' }, /* XK_dead_E */
  10333. { 0xfe84, 'i' }, /* XK_dead_i */
  10334. { 0xfe85, 'I' }, /* XK_dead_I */
  10335. { 0xfe86, 'o' }, /* XK_dead_o */
  10336. { 0xfe87, 'O' }, /* XK_dead_O */
  10337. { 0xfe88, 'u' }, /* XK_dead_u */
  10338. { 0xfe89, 'U' }, /* XK_dead_U */
  10339. { 0xfe8a, 0x0259 },
  10340. { 0xfe8b, 0x018f },
  10341. { 0xfe8c, 0x00b5 },
  10342. { 0xfe90, '_' },
  10343. { 0xfe91, 0x02c8 },
  10344. { 0xfe92, 0x02cc },
  10345. { 0xff80 /*XKB_KEY_KP_Space*/, ' ' },
  10346. { 0xff95 /*XKB_KEY_KP_7*/, 0x0037 },
  10347. { 0xff96 /*XKB_KEY_KP_4*/, 0x0034 },
  10348. { 0xff97 /*XKB_KEY_KP_8*/, 0x0038 },
  10349. { 0xff98 /*XKB_KEY_KP_6*/, 0x0036 },
  10350. { 0xff99 /*XKB_KEY_KP_2*/, 0x0032 },
  10351. { 0xff9a /*XKB_KEY_KP_9*/, 0x0039 },
  10352. { 0xff9b /*XKB_KEY_KP_3*/, 0x0033 },
  10353. { 0xff9c /*XKB_KEY_KP_1*/, 0x0031 },
  10354. { 0xff9d /*XKB_KEY_KP_5*/, 0x0035 },
  10355. { 0xff9e /*XKB_KEY_KP_0*/, 0x0030 },
  10356. { 0xffaa /*XKB_KEY_KP_Multiply*/, '*' },
  10357. { 0xffab /*XKB_KEY_KP_Add*/, '+' },
  10358. { 0xffac /*XKB_KEY_KP_Separator*/, ',' },
  10359. { 0xffad /*XKB_KEY_KP_Subtract*/, '-' },
  10360. { 0xffae /*XKB_KEY_KP_Decimal*/, '.' },
  10361. { 0xffaf /*XKB_KEY_KP_Divide*/, '/' },
  10362. { 0xffb0 /*XKB_KEY_KP_0*/, 0x0030 },
  10363. { 0xffb1 /*XKB_KEY_KP_1*/, 0x0031 },
  10364. { 0xffb2 /*XKB_KEY_KP_2*/, 0x0032 },
  10365. { 0xffb3 /*XKB_KEY_KP_3*/, 0x0033 },
  10366. { 0xffb4 /*XKB_KEY_KP_4*/, 0x0034 },
  10367. { 0xffb5 /*XKB_KEY_KP_5*/, 0x0035 },
  10368. { 0xffb6 /*XKB_KEY_KP_6*/, 0x0036 },
  10369. { 0xffb7 /*XKB_KEY_KP_7*/, 0x0037 },
  10370. { 0xffb8 /*XKB_KEY_KP_8*/, 0x0038 },
  10371. { 0xffb9 /*XKB_KEY_KP_9*/, 0x0039 },
  10372. { 0xffbd /*XKB_KEY_KP_Equal*/, '=' }
  10373. };
  10374. _SOKOL_PRIVATE int _sapp_x11_error_handler(Display* display, XErrorEvent* event) {
  10375. _SOKOL_UNUSED(display);
  10376. _sapp.x11.error_code = event->error_code;
  10377. return 0;
  10378. }
  10379. _SOKOL_PRIVATE void _sapp_x11_grab_error_handler(void) {
  10380. _sapp.x11.error_code = Success;
  10381. XSetErrorHandler(_sapp_x11_error_handler);
  10382. }
  10383. _SOKOL_PRIVATE void _sapp_x11_release_error_handler(void) {
  10384. XSync(_sapp.x11.display, False);
  10385. XSetErrorHandler(NULL);
  10386. }
  10387. _SOKOL_PRIVATE void _sapp_x11_init_extensions(void) {
  10388. _sapp.x11.UTF8_STRING = XInternAtom(_sapp.x11.display, "UTF8_STRING", False);
  10389. _sapp.x11.WM_PROTOCOLS = XInternAtom(_sapp.x11.display, "WM_PROTOCOLS", False);
  10390. _sapp.x11.WM_DELETE_WINDOW = XInternAtom(_sapp.x11.display, "WM_DELETE_WINDOW", False);
  10391. _sapp.x11.WM_STATE = XInternAtom(_sapp.x11.display, "WM_STATE", False);
  10392. _sapp.x11.NET_WM_NAME = XInternAtom(_sapp.x11.display, "_NET_WM_NAME", False);
  10393. _sapp.x11.NET_WM_ICON_NAME = XInternAtom(_sapp.x11.display, "_NET_WM_ICON_NAME", False);
  10394. _sapp.x11.NET_WM_ICON = XInternAtom(_sapp.x11.display, "_NET_WM_ICON", False);
  10395. _sapp.x11.NET_WM_STATE = XInternAtom(_sapp.x11.display, "_NET_WM_STATE", False);
  10396. _sapp.x11.NET_WM_STATE_FULLSCREEN = XInternAtom(_sapp.x11.display, "_NET_WM_STATE_FULLSCREEN", False);
  10397. _sapp.x11.CLIPBOARD = XInternAtom(_sapp.x11.display, "CLIPBOARD", False);
  10398. _sapp.x11.TARGETS = XInternAtom(_sapp.x11.display, "TARGETS", False);
  10399. if (_sapp.drop.enabled) {
  10400. _sapp.x11.xdnd.XdndAware = XInternAtom(_sapp.x11.display, "XdndAware", False);
  10401. _sapp.x11.xdnd.XdndEnter = XInternAtom(_sapp.x11.display, "XdndEnter", False);
  10402. _sapp.x11.xdnd.XdndPosition = XInternAtom(_sapp.x11.display, "XdndPosition", False);
  10403. _sapp.x11.xdnd.XdndStatus = XInternAtom(_sapp.x11.display, "XdndStatus", False);
  10404. _sapp.x11.xdnd.XdndActionCopy = XInternAtom(_sapp.x11.display, "XdndActionCopy", False);
  10405. _sapp.x11.xdnd.XdndDrop = XInternAtom(_sapp.x11.display, "XdndDrop", False);
  10406. _sapp.x11.xdnd.XdndFinished = XInternAtom(_sapp.x11.display, "XdndFinished", False);
  10407. _sapp.x11.xdnd.XdndSelection = XInternAtom(_sapp.x11.display, "XdndSelection", False);
  10408. _sapp.x11.xdnd.XdndTypeList = XInternAtom(_sapp.x11.display, "XdndTypeList", False);
  10409. _sapp.x11.xdnd.text_uri_list = XInternAtom(_sapp.x11.display, "text/uri-list", False);
  10410. }
  10411. /* check Xi extension for raw mouse input */
  10412. if (XQueryExtension(_sapp.x11.display, "XInputExtension", &_sapp.x11.xi.major_opcode, &_sapp.x11.xi.event_base, &_sapp.x11.xi.error_base)) {
  10413. _sapp.x11.xi.major = 2;
  10414. _sapp.x11.xi.minor = 0;
  10415. if (XIQueryVersion(_sapp.x11.display, &_sapp.x11.xi.major, &_sapp.x11.xi.minor) == Success) {
  10416. _sapp.x11.xi.available = true;
  10417. }
  10418. }
  10419. }
  10420. // translate the X11 KeySyms for a key to sokol-app key code
  10421. // NOTE: this is only used as a fallback, in case the XBK method fails
  10422. // it is layout-dependent and will fail partially on most non-US layouts.
  10423. //
  10424. _SOKOL_PRIVATE sapp_keycode _sapp_x11_translate_keysyms(const KeySym* keysyms, int width) {
  10425. if (width > 1) {
  10426. switch (keysyms[1]) {
  10427. case XK_KP_0: return SAPP_KEYCODE_KP_0;
  10428. case XK_KP_1: return SAPP_KEYCODE_KP_1;
  10429. case XK_KP_2: return SAPP_KEYCODE_KP_2;
  10430. case XK_KP_3: return SAPP_KEYCODE_KP_3;
  10431. case XK_KP_4: return SAPP_KEYCODE_KP_4;
  10432. case XK_KP_5: return SAPP_KEYCODE_KP_5;
  10433. case XK_KP_6: return SAPP_KEYCODE_KP_6;
  10434. case XK_KP_7: return SAPP_KEYCODE_KP_7;
  10435. case XK_KP_8: return SAPP_KEYCODE_KP_8;
  10436. case XK_KP_9: return SAPP_KEYCODE_KP_9;
  10437. case XK_KP_Separator:
  10438. case XK_KP_Decimal: return SAPP_KEYCODE_KP_DECIMAL;
  10439. case XK_KP_Equal: return SAPP_KEYCODE_KP_EQUAL;
  10440. case XK_KP_Enter: return SAPP_KEYCODE_KP_ENTER;
  10441. default: break;
  10442. }
  10443. }
  10444. switch (keysyms[0]) {
  10445. case XK_Escape: return SAPP_KEYCODE_ESCAPE;
  10446. case XK_Tab: return SAPP_KEYCODE_TAB;
  10447. case XK_Shift_L: return SAPP_KEYCODE_LEFT_SHIFT;
  10448. case XK_Shift_R: return SAPP_KEYCODE_RIGHT_SHIFT;
  10449. case XK_Control_L: return SAPP_KEYCODE_LEFT_CONTROL;
  10450. case XK_Control_R: return SAPP_KEYCODE_RIGHT_CONTROL;
  10451. case XK_Meta_L:
  10452. case XK_Alt_L: return SAPP_KEYCODE_LEFT_ALT;
  10453. case XK_Mode_switch: // Mapped to Alt_R on many keyboards
  10454. case XK_ISO_Level3_Shift: // AltGr on at least some machines
  10455. case XK_Meta_R:
  10456. case XK_Alt_R: return SAPP_KEYCODE_RIGHT_ALT;
  10457. case XK_Super_L: return SAPP_KEYCODE_LEFT_SUPER;
  10458. case XK_Super_R: return SAPP_KEYCODE_RIGHT_SUPER;
  10459. case XK_Menu: return SAPP_KEYCODE_MENU;
  10460. case XK_Num_Lock: return SAPP_KEYCODE_NUM_LOCK;
  10461. case XK_Caps_Lock: return SAPP_KEYCODE_CAPS_LOCK;
  10462. case XK_Print: return SAPP_KEYCODE_PRINT_SCREEN;
  10463. case XK_Scroll_Lock: return SAPP_KEYCODE_SCROLL_LOCK;
  10464. case XK_Pause: return SAPP_KEYCODE_PAUSE;
  10465. case XK_Delete: return SAPP_KEYCODE_DELETE;
  10466. case XK_BackSpace: return SAPP_KEYCODE_BACKSPACE;
  10467. case XK_Return: return SAPP_KEYCODE_ENTER;
  10468. case XK_Home: return SAPP_KEYCODE_HOME;
  10469. case XK_End: return SAPP_KEYCODE_END;
  10470. case XK_Page_Up: return SAPP_KEYCODE_PAGE_UP;
  10471. case XK_Page_Down: return SAPP_KEYCODE_PAGE_DOWN;
  10472. case XK_Insert: return SAPP_KEYCODE_INSERT;
  10473. case XK_Left: return SAPP_KEYCODE_LEFT;
  10474. case XK_Right: return SAPP_KEYCODE_RIGHT;
  10475. case XK_Down: return SAPP_KEYCODE_DOWN;
  10476. case XK_Up: return SAPP_KEYCODE_UP;
  10477. case XK_F1: return SAPP_KEYCODE_F1;
  10478. case XK_F2: return SAPP_KEYCODE_F2;
  10479. case XK_F3: return SAPP_KEYCODE_F3;
  10480. case XK_F4: return SAPP_KEYCODE_F4;
  10481. case XK_F5: return SAPP_KEYCODE_F5;
  10482. case XK_F6: return SAPP_KEYCODE_F6;
  10483. case XK_F7: return SAPP_KEYCODE_F7;
  10484. case XK_F8: return SAPP_KEYCODE_F8;
  10485. case XK_F9: return SAPP_KEYCODE_F9;
  10486. case XK_F10: return SAPP_KEYCODE_F10;
  10487. case XK_F11: return SAPP_KEYCODE_F11;
  10488. case XK_F12: return SAPP_KEYCODE_F12;
  10489. case XK_F13: return SAPP_KEYCODE_F13;
  10490. case XK_F14: return SAPP_KEYCODE_F14;
  10491. case XK_F15: return SAPP_KEYCODE_F15;
  10492. case XK_F16: return SAPP_KEYCODE_F16;
  10493. case XK_F17: return SAPP_KEYCODE_F17;
  10494. case XK_F18: return SAPP_KEYCODE_F18;
  10495. case XK_F19: return SAPP_KEYCODE_F19;
  10496. case XK_F20: return SAPP_KEYCODE_F20;
  10497. case XK_F21: return SAPP_KEYCODE_F21;
  10498. case XK_F22: return SAPP_KEYCODE_F22;
  10499. case XK_F23: return SAPP_KEYCODE_F23;
  10500. case XK_F24: return SAPP_KEYCODE_F24;
  10501. case XK_F25: return SAPP_KEYCODE_F25;
  10502. // numeric keypad
  10503. case XK_KP_Divide: return SAPP_KEYCODE_KP_DIVIDE;
  10504. case XK_KP_Multiply: return SAPP_KEYCODE_KP_MULTIPLY;
  10505. case XK_KP_Subtract: return SAPP_KEYCODE_KP_SUBTRACT;
  10506. case XK_KP_Add: return SAPP_KEYCODE_KP_ADD;
  10507. // these should have been detected in secondary keysym test above!
  10508. case XK_KP_Insert: return SAPP_KEYCODE_KP_0;
  10509. case XK_KP_End: return SAPP_KEYCODE_KP_1;
  10510. case XK_KP_Down: return SAPP_KEYCODE_KP_2;
  10511. case XK_KP_Page_Down: return SAPP_KEYCODE_KP_3;
  10512. case XK_KP_Left: return SAPP_KEYCODE_KP_4;
  10513. case XK_KP_Right: return SAPP_KEYCODE_KP_6;
  10514. case XK_KP_Home: return SAPP_KEYCODE_KP_7;
  10515. case XK_KP_Up: return SAPP_KEYCODE_KP_8;
  10516. case XK_KP_Page_Up: return SAPP_KEYCODE_KP_9;
  10517. case XK_KP_Delete: return SAPP_KEYCODE_KP_DECIMAL;
  10518. case XK_KP_Equal: return SAPP_KEYCODE_KP_EQUAL;
  10519. case XK_KP_Enter: return SAPP_KEYCODE_KP_ENTER;
  10520. // last resort: Check for printable keys (should not happen if the XKB
  10521. // extension is available). This will give a layout dependent mapping
  10522. // (which is wrong, and we may miss some keys, especially on non-US
  10523. // keyboards), but it's better than nothing...
  10524. case XK_a: return SAPP_KEYCODE_A;
  10525. case XK_b: return SAPP_KEYCODE_B;
  10526. case XK_c: return SAPP_KEYCODE_C;
  10527. case XK_d: return SAPP_KEYCODE_D;
  10528. case XK_e: return SAPP_KEYCODE_E;
  10529. case XK_f: return SAPP_KEYCODE_F;
  10530. case XK_g: return SAPP_KEYCODE_G;
  10531. case XK_h: return SAPP_KEYCODE_H;
  10532. case XK_i: return SAPP_KEYCODE_I;
  10533. case XK_j: return SAPP_KEYCODE_J;
  10534. case XK_k: return SAPP_KEYCODE_K;
  10535. case XK_l: return SAPP_KEYCODE_L;
  10536. case XK_m: return SAPP_KEYCODE_M;
  10537. case XK_n: return SAPP_KEYCODE_N;
  10538. case XK_o: return SAPP_KEYCODE_O;
  10539. case XK_p: return SAPP_KEYCODE_P;
  10540. case XK_q: return SAPP_KEYCODE_Q;
  10541. case XK_r: return SAPP_KEYCODE_R;
  10542. case XK_s: return SAPP_KEYCODE_S;
  10543. case XK_t: return SAPP_KEYCODE_T;
  10544. case XK_u: return SAPP_KEYCODE_U;
  10545. case XK_v: return SAPP_KEYCODE_V;
  10546. case XK_w: return SAPP_KEYCODE_W;
  10547. case XK_x: return SAPP_KEYCODE_X;
  10548. case XK_y: return SAPP_KEYCODE_Y;
  10549. case XK_z: return SAPP_KEYCODE_Z;
  10550. case XK_1: return SAPP_KEYCODE_1;
  10551. case XK_2: return SAPP_KEYCODE_2;
  10552. case XK_3: return SAPP_KEYCODE_3;
  10553. case XK_4: return SAPP_KEYCODE_4;
  10554. case XK_5: return SAPP_KEYCODE_5;
  10555. case XK_6: return SAPP_KEYCODE_6;
  10556. case XK_7: return SAPP_KEYCODE_7;
  10557. case XK_8: return SAPP_KEYCODE_8;
  10558. case XK_9: return SAPP_KEYCODE_9;
  10559. case XK_0: return SAPP_KEYCODE_0;
  10560. case XK_space: return SAPP_KEYCODE_SPACE;
  10561. case XK_minus: return SAPP_KEYCODE_MINUS;
  10562. case XK_equal: return SAPP_KEYCODE_EQUAL;
  10563. case XK_bracketleft: return SAPP_KEYCODE_LEFT_BRACKET;
  10564. case XK_bracketright: return SAPP_KEYCODE_RIGHT_BRACKET;
  10565. case XK_backslash: return SAPP_KEYCODE_BACKSLASH;
  10566. case XK_semicolon: return SAPP_KEYCODE_SEMICOLON;
  10567. case XK_apostrophe: return SAPP_KEYCODE_APOSTROPHE;
  10568. case XK_grave: return SAPP_KEYCODE_GRAVE_ACCENT;
  10569. case XK_comma: return SAPP_KEYCODE_COMMA;
  10570. case XK_period: return SAPP_KEYCODE_PERIOD;
  10571. case XK_slash: return SAPP_KEYCODE_SLASH;
  10572. case XK_less: return SAPP_KEYCODE_WORLD_1; // At least in some layouts...
  10573. default: break;
  10574. }
  10575. // no matching translation was found
  10576. return SAPP_KEYCODE_INVALID;
  10577. }
  10578. // setup dynamic keycode/scancode mapping tables, this is required
  10579. // for getting layout-independent keycodes on X11.
  10580. //
  10581. // see GLFW x11_init.c/createKeyTables()
  10582. _SOKOL_PRIVATE void _sapp_x11_init_keytable(void) {
  10583. for (int i = 0; i < SAPP_MAX_KEYCODES; i++) {
  10584. _sapp.keycodes[i] = SAPP_KEYCODE_INVALID;
  10585. }
  10586. // use XKB to determine physical key locations independently of the current keyboard layout
  10587. XkbDescPtr desc = XkbGetMap(_sapp.x11.display, 0, XkbUseCoreKbd);
  10588. SOKOL_ASSERT(desc);
  10589. XkbGetNames(_sapp.x11.display, XkbKeyNamesMask | XkbKeyAliasesMask, desc);
  10590. const int scancode_min = desc->min_key_code;
  10591. const int scancode_max = desc->max_key_code;
  10592. const struct { sapp_keycode key; const char* name; } keymap[] = {
  10593. { SAPP_KEYCODE_GRAVE_ACCENT, "TLDE" },
  10594. { SAPP_KEYCODE_1, "AE01" },
  10595. { SAPP_KEYCODE_2, "AE02" },
  10596. { SAPP_KEYCODE_3, "AE03" },
  10597. { SAPP_KEYCODE_4, "AE04" },
  10598. { SAPP_KEYCODE_5, "AE05" },
  10599. { SAPP_KEYCODE_6, "AE06" },
  10600. { SAPP_KEYCODE_7, "AE07" },
  10601. { SAPP_KEYCODE_8, "AE08" },
  10602. { SAPP_KEYCODE_9, "AE09" },
  10603. { SAPP_KEYCODE_0, "AE10" },
  10604. { SAPP_KEYCODE_MINUS, "AE11" },
  10605. { SAPP_KEYCODE_EQUAL, "AE12" },
  10606. { SAPP_KEYCODE_Q, "AD01" },
  10607. { SAPP_KEYCODE_W, "AD02" },
  10608. { SAPP_KEYCODE_E, "AD03" },
  10609. { SAPP_KEYCODE_R, "AD04" },
  10610. { SAPP_KEYCODE_T, "AD05" },
  10611. { SAPP_KEYCODE_Y, "AD06" },
  10612. { SAPP_KEYCODE_U, "AD07" },
  10613. { SAPP_KEYCODE_I, "AD08" },
  10614. { SAPP_KEYCODE_O, "AD09" },
  10615. { SAPP_KEYCODE_P, "AD10" },
  10616. { SAPP_KEYCODE_LEFT_BRACKET, "AD11" },
  10617. { SAPP_KEYCODE_RIGHT_BRACKET, "AD12" },
  10618. { SAPP_KEYCODE_A, "AC01" },
  10619. { SAPP_KEYCODE_S, "AC02" },
  10620. { SAPP_KEYCODE_D, "AC03" },
  10621. { SAPP_KEYCODE_F, "AC04" },
  10622. { SAPP_KEYCODE_G, "AC05" },
  10623. { SAPP_KEYCODE_H, "AC06" },
  10624. { SAPP_KEYCODE_J, "AC07" },
  10625. { SAPP_KEYCODE_K, "AC08" },
  10626. { SAPP_KEYCODE_L, "AC09" },
  10627. { SAPP_KEYCODE_SEMICOLON, "AC10" },
  10628. { SAPP_KEYCODE_APOSTROPHE, "AC11" },
  10629. { SAPP_KEYCODE_Z, "AB01" },
  10630. { SAPP_KEYCODE_X, "AB02" },
  10631. { SAPP_KEYCODE_C, "AB03" },
  10632. { SAPP_KEYCODE_V, "AB04" },
  10633. { SAPP_KEYCODE_B, "AB05" },
  10634. { SAPP_KEYCODE_N, "AB06" },
  10635. { SAPP_KEYCODE_M, "AB07" },
  10636. { SAPP_KEYCODE_COMMA, "AB08" },
  10637. { SAPP_KEYCODE_PERIOD, "AB09" },
  10638. { SAPP_KEYCODE_SLASH, "AB10" },
  10639. { SAPP_KEYCODE_BACKSLASH, "BKSL" },
  10640. { SAPP_KEYCODE_WORLD_1, "LSGT" },
  10641. { SAPP_KEYCODE_SPACE, "SPCE" },
  10642. { SAPP_KEYCODE_ESCAPE, "ESC" },
  10643. { SAPP_KEYCODE_ENTER, "RTRN" },
  10644. { SAPP_KEYCODE_TAB, "TAB" },
  10645. { SAPP_KEYCODE_BACKSPACE, "BKSP" },
  10646. { SAPP_KEYCODE_INSERT, "INS" },
  10647. { SAPP_KEYCODE_DELETE, "DELE" },
  10648. { SAPP_KEYCODE_RIGHT, "RGHT" },
  10649. { SAPP_KEYCODE_LEFT, "LEFT" },
  10650. { SAPP_KEYCODE_DOWN, "DOWN" },
  10651. { SAPP_KEYCODE_UP, "UP" },
  10652. { SAPP_KEYCODE_PAGE_UP, "PGUP" },
  10653. { SAPP_KEYCODE_PAGE_DOWN, "PGDN" },
  10654. { SAPP_KEYCODE_HOME, "HOME" },
  10655. { SAPP_KEYCODE_END, "END" },
  10656. { SAPP_KEYCODE_CAPS_LOCK, "CAPS" },
  10657. { SAPP_KEYCODE_SCROLL_LOCK, "SCLK" },
  10658. { SAPP_KEYCODE_NUM_LOCK, "NMLK" },
  10659. { SAPP_KEYCODE_PRINT_SCREEN, "PRSC" },
  10660. { SAPP_KEYCODE_PAUSE, "PAUS" },
  10661. { SAPP_KEYCODE_F1, "FK01" },
  10662. { SAPP_KEYCODE_F2, "FK02" },
  10663. { SAPP_KEYCODE_F3, "FK03" },
  10664. { SAPP_KEYCODE_F4, "FK04" },
  10665. { SAPP_KEYCODE_F5, "FK05" },
  10666. { SAPP_KEYCODE_F6, "FK06" },
  10667. { SAPP_KEYCODE_F7, "FK07" },
  10668. { SAPP_KEYCODE_F8, "FK08" },
  10669. { SAPP_KEYCODE_F9, "FK09" },
  10670. { SAPP_KEYCODE_F10, "FK10" },
  10671. { SAPP_KEYCODE_F11, "FK11" },
  10672. { SAPP_KEYCODE_F12, "FK12" },
  10673. { SAPP_KEYCODE_F13, "FK13" },
  10674. { SAPP_KEYCODE_F14, "FK14" },
  10675. { SAPP_KEYCODE_F15, "FK15" },
  10676. { SAPP_KEYCODE_F16, "FK16" },
  10677. { SAPP_KEYCODE_F17, "FK17" },
  10678. { SAPP_KEYCODE_F18, "FK18" },
  10679. { SAPP_KEYCODE_F19, "FK19" },
  10680. { SAPP_KEYCODE_F20, "FK20" },
  10681. { SAPP_KEYCODE_F21, "FK21" },
  10682. { SAPP_KEYCODE_F22, "FK22" },
  10683. { SAPP_KEYCODE_F23, "FK23" },
  10684. { SAPP_KEYCODE_F24, "FK24" },
  10685. { SAPP_KEYCODE_F25, "FK25" },
  10686. { SAPP_KEYCODE_KP_0, "KP0" },
  10687. { SAPP_KEYCODE_KP_1, "KP1" },
  10688. { SAPP_KEYCODE_KP_2, "KP2" },
  10689. { SAPP_KEYCODE_KP_3, "KP3" },
  10690. { SAPP_KEYCODE_KP_4, "KP4" },
  10691. { SAPP_KEYCODE_KP_5, "KP5" },
  10692. { SAPP_KEYCODE_KP_6, "KP6" },
  10693. { SAPP_KEYCODE_KP_7, "KP7" },
  10694. { SAPP_KEYCODE_KP_8, "KP8" },
  10695. { SAPP_KEYCODE_KP_9, "KP9" },
  10696. { SAPP_KEYCODE_KP_DECIMAL, "KPDL" },
  10697. { SAPP_KEYCODE_KP_DIVIDE, "KPDV" },
  10698. { SAPP_KEYCODE_KP_MULTIPLY, "KPMU" },
  10699. { SAPP_KEYCODE_KP_SUBTRACT, "KPSU" },
  10700. { SAPP_KEYCODE_KP_ADD, "KPAD" },
  10701. { SAPP_KEYCODE_KP_ENTER, "KPEN" },
  10702. { SAPP_KEYCODE_KP_EQUAL, "KPEQ" },
  10703. { SAPP_KEYCODE_LEFT_SHIFT, "LFSH" },
  10704. { SAPP_KEYCODE_LEFT_CONTROL, "LCTL" },
  10705. { SAPP_KEYCODE_LEFT_ALT, "LALT" },
  10706. { SAPP_KEYCODE_LEFT_SUPER, "LWIN" },
  10707. { SAPP_KEYCODE_RIGHT_SHIFT, "RTSH" },
  10708. { SAPP_KEYCODE_RIGHT_CONTROL, "RCTL" },
  10709. { SAPP_KEYCODE_RIGHT_ALT, "RALT" },
  10710. { SAPP_KEYCODE_RIGHT_ALT, "LVL3" },
  10711. { SAPP_KEYCODE_RIGHT_ALT, "MDSW" },
  10712. { SAPP_KEYCODE_RIGHT_SUPER, "RWIN" },
  10713. { SAPP_KEYCODE_MENU, "MENU" }
  10714. };
  10715. const int num_keymap_items = (int)(sizeof(keymap) / sizeof(keymap[0]));
  10716. // find X11 keycode to sokol-app key code mapping
  10717. for (int scancode = scancode_min; scancode <= scancode_max; scancode++) {
  10718. sapp_keycode key = SAPP_KEYCODE_INVALID;
  10719. for (int i = 0; i < num_keymap_items; i++) {
  10720. if (strncmp(desc->names->keys[scancode].name, keymap[i].name, XkbKeyNameLength) == 0) {
  10721. key = keymap[i].key;
  10722. break;
  10723. }
  10724. }
  10725. // fall back to key aliases in case the key name did not match
  10726. for (int i = 0; i < desc->names->num_key_aliases; i++) {
  10727. if (key != SAPP_KEYCODE_INVALID) {
  10728. break;
  10729. }
  10730. if (strncmp(desc->names->key_aliases[i].real, desc->names->keys[scancode].name, XkbKeyNameLength) != 0) {
  10731. continue;
  10732. }
  10733. for (int j = 0; j < num_keymap_items; j++) {
  10734. if (strncmp(desc->names->key_aliases[i].alias, keymap[i].name, XkbKeyNameLength) == 0) {
  10735. key = keymap[i].key;
  10736. break;
  10737. }
  10738. }
  10739. }
  10740. _sapp.keycodes[scancode] = key;
  10741. }
  10742. XkbFreeNames(desc, XkbKeyNamesMask, True);
  10743. XkbFreeKeyboard(desc, 0, True);
  10744. int width = 0;
  10745. KeySym* keysyms = XGetKeyboardMapping(_sapp.x11.display, scancode_min, scancode_max - scancode_min + 1, &width);
  10746. for (int scancode = scancode_min; scancode <= scancode_max; scancode++) {
  10747. // translate untranslated key codes using the traditional X11 KeySym lookups
  10748. if (_sapp.keycodes[scancode] == SAPP_KEYCODE_INVALID) {
  10749. const size_t base = (size_t)((scancode - scancode_min) * width);
  10750. _sapp.keycodes[scancode] = _sapp_x11_translate_keysyms(&keysyms[base], width);
  10751. }
  10752. }
  10753. XFree(keysyms);
  10754. }
  10755. _SOKOL_PRIVATE void _sapp_x11_query_system_dpi(void) {
  10756. /* from GLFW:
  10757. NOTE: Default to the display-wide DPI as we don't currently have a policy
  10758. for which monitor a window is considered to be on
  10759. _sapp.x11.dpi = DisplayWidth(_sapp.x11.display, _sapp.x11.screen) *
  10760. 25.4f / DisplayWidthMM(_sapp.x11.display, _sapp.x11.screen);
  10761. NOTE: Basing the scale on Xft.dpi where available should provide the most
  10762. consistent user experience (matches Qt, Gtk, etc), although not
  10763. always the most accurate one
  10764. */
  10765. bool dpi_ok = false;
  10766. char* rms = XResourceManagerString(_sapp.x11.display);
  10767. if (rms) {
  10768. XrmDatabase db = XrmGetStringDatabase(rms);
  10769. if (db) {
  10770. XrmValue value;
  10771. char* type = NULL;
  10772. if (XrmGetResource(db, "Xft.dpi", "Xft.Dpi", &type, &value)) {
  10773. if (type && strcmp(type, "String") == 0) {
  10774. _sapp.x11.dpi = atof(value.addr);
  10775. dpi_ok = true;
  10776. }
  10777. }
  10778. XrmDestroyDatabase(db);
  10779. }
  10780. }
  10781. // fallback if querying DPI had failed: assume the standard DPI 96.0f
  10782. if (!dpi_ok) {
  10783. _sapp.x11.dpi = 96.0f;
  10784. _SAPP_WARN(LINUX_X11_QUERY_SYSTEM_DPI_FAILED);
  10785. }
  10786. }
  10787. #if defined(_SAPP_GLX)
  10788. _SOKOL_PRIVATE bool _sapp_glx_has_ext(const char* ext, const char* extensions) {
  10789. SOKOL_ASSERT(ext);
  10790. const char* start = extensions;
  10791. while (true) {
  10792. const char* where = strstr(start, ext);
  10793. if (!where) {
  10794. return false;
  10795. }
  10796. const char* terminator = where + strlen(ext);
  10797. if ((where == start) || (*(where - 1) == ' ')) {
  10798. if (*terminator == ' ' || *terminator == '\0') {
  10799. break;
  10800. }
  10801. }
  10802. start = terminator;
  10803. }
  10804. return true;
  10805. }
  10806. _SOKOL_PRIVATE bool _sapp_glx_extsupported(const char* ext, const char* extensions) {
  10807. if (extensions) {
  10808. return _sapp_glx_has_ext(ext, extensions);
  10809. } else {
  10810. return false;
  10811. }
  10812. }
  10813. _SOKOL_PRIVATE void* _sapp_glx_getprocaddr(const char* procname)
  10814. {
  10815. if (_sapp.glx.GetProcAddress) {
  10816. return (void*) _sapp.glx.GetProcAddress(procname);
  10817. } else if (_sapp.glx.GetProcAddressARB) {
  10818. return (void*) _sapp.glx.GetProcAddressARB(procname);
  10819. } else {
  10820. return dlsym(_sapp.glx.libgl, procname);
  10821. }
  10822. }
  10823. _SOKOL_PRIVATE void _sapp_glx_init(void) {
  10824. const char* sonames[] = { "libGL.so.1", "libGL.so", 0 };
  10825. for (int i = 0; sonames[i]; i++) {
  10826. _sapp.glx.libgl = dlopen(sonames[i], RTLD_LAZY|RTLD_GLOBAL);
  10827. if (_sapp.glx.libgl) {
  10828. break;
  10829. }
  10830. }
  10831. if (!_sapp.glx.libgl) {
  10832. _SAPP_PANIC(LINUX_GLX_LOAD_LIBGL_FAILED);
  10833. }
  10834. _sapp.glx.GetFBConfigs = (PFNGLXGETFBCONFIGSPROC) dlsym(_sapp.glx.libgl, "glXGetFBConfigs");
  10835. _sapp.glx.GetFBConfigAttrib = (PFNGLXGETFBCONFIGATTRIBPROC) dlsym(_sapp.glx.libgl, "glXGetFBConfigAttrib");
  10836. _sapp.glx.GetClientString = (PFNGLXGETCLIENTSTRINGPROC) dlsym(_sapp.glx.libgl, "glXGetClientString");
  10837. _sapp.glx.QueryExtension = (PFNGLXQUERYEXTENSIONPROC) dlsym(_sapp.glx.libgl, "glXQueryExtension");
  10838. _sapp.glx.QueryVersion = (PFNGLXQUERYVERSIONPROC) dlsym(_sapp.glx.libgl, "glXQueryVersion");
  10839. _sapp.glx.DestroyContext = (PFNGLXDESTROYCONTEXTPROC) dlsym(_sapp.glx.libgl, "glXDestroyContext");
  10840. _sapp.glx.MakeCurrent = (PFNGLXMAKECURRENTPROC) dlsym(_sapp.glx.libgl, "glXMakeCurrent");
  10841. _sapp.glx.SwapBuffers = (PFNGLXSWAPBUFFERSPROC) dlsym(_sapp.glx.libgl, "glXSwapBuffers");
  10842. _sapp.glx.QueryExtensionsString = (PFNGLXQUERYEXTENSIONSSTRINGPROC) dlsym(_sapp.glx.libgl, "glXQueryExtensionsString");
  10843. _sapp.glx.CreateWindow = (PFNGLXCREATEWINDOWPROC) dlsym(_sapp.glx.libgl, "glXCreateWindow");
  10844. _sapp.glx.DestroyWindow = (PFNGLXDESTROYWINDOWPROC) dlsym(_sapp.glx.libgl, "glXDestroyWindow");
  10845. _sapp.glx.GetProcAddress = (PFNGLXGETPROCADDRESSPROC) dlsym(_sapp.glx.libgl, "glXGetProcAddress");
  10846. _sapp.glx.GetProcAddressARB = (PFNGLXGETPROCADDRESSPROC) dlsym(_sapp.glx.libgl, "glXGetProcAddressARB");
  10847. _sapp.glx.GetVisualFromFBConfig = (PFNGLXGETVISUALFROMFBCONFIGPROC) dlsym(_sapp.glx.libgl, "glXGetVisualFromFBConfig");
  10848. if (!_sapp.glx.GetFBConfigs ||
  10849. !_sapp.glx.GetFBConfigAttrib ||
  10850. !_sapp.glx.GetClientString ||
  10851. !_sapp.glx.QueryExtension ||
  10852. !_sapp.glx.QueryVersion ||
  10853. !_sapp.glx.DestroyContext ||
  10854. !_sapp.glx.MakeCurrent ||
  10855. !_sapp.glx.SwapBuffers ||
  10856. !_sapp.glx.QueryExtensionsString ||
  10857. !_sapp.glx.CreateWindow ||
  10858. !_sapp.glx.DestroyWindow ||
  10859. !_sapp.glx.GetProcAddress ||
  10860. !_sapp.glx.GetProcAddressARB ||
  10861. !_sapp.glx.GetVisualFromFBConfig)
  10862. {
  10863. _SAPP_PANIC(LINUX_GLX_LOAD_ENTRY_POINTS_FAILED);
  10864. }
  10865. if (!_sapp.glx.QueryExtension(_sapp.x11.display, &_sapp.glx.error_base, &_sapp.glx.event_base)) {
  10866. _SAPP_PANIC(LINUX_GLX_EXTENSION_NOT_FOUND);
  10867. }
  10868. if (!_sapp.glx.QueryVersion(_sapp.x11.display, &_sapp.glx.major, &_sapp.glx.minor)) {
  10869. _SAPP_PANIC(LINUX_GLX_QUERY_VERSION_FAILED);
  10870. }
  10871. if (_sapp.glx.major == 1 && _sapp.glx.minor < 3) {
  10872. _SAPP_PANIC(LINUX_GLX_VERSION_TOO_LOW);
  10873. }
  10874. const char* exts = _sapp.glx.QueryExtensionsString(_sapp.x11.display, _sapp.x11.screen);
  10875. if (_sapp_glx_extsupported("GLX_EXT_swap_control", exts)) {
  10876. _sapp.glx.SwapIntervalEXT = (PFNGLXSWAPINTERVALEXTPROC) _sapp_glx_getprocaddr("glXSwapIntervalEXT");
  10877. _sapp.glx.EXT_swap_control = 0 != _sapp.glx.SwapIntervalEXT;
  10878. }
  10879. if (_sapp_glx_extsupported("GLX_MESA_swap_control", exts)) {
  10880. _sapp.glx.SwapIntervalMESA = (PFNGLXSWAPINTERVALMESAPROC) _sapp_glx_getprocaddr("glXSwapIntervalMESA");
  10881. _sapp.glx.MESA_swap_control = 0 != _sapp.glx.SwapIntervalMESA;
  10882. }
  10883. _sapp.glx.ARB_multisample = _sapp_glx_extsupported("GLX_ARB_multisample", exts);
  10884. if (_sapp_glx_extsupported("GLX_ARB_create_context", exts)) {
  10885. _sapp.glx.CreateContextAttribsARB = (PFNGLXCREATECONTEXTATTRIBSARBPROC) _sapp_glx_getprocaddr("glXCreateContextAttribsARB");
  10886. _sapp.glx.ARB_create_context = 0 != _sapp.glx.CreateContextAttribsARB;
  10887. }
  10888. _sapp.glx.ARB_create_context_profile = _sapp_glx_extsupported("GLX_ARB_create_context_profile", exts);
  10889. }
  10890. _SOKOL_PRIVATE int _sapp_glx_attrib(GLXFBConfig fbconfig, int attrib) {
  10891. int value;
  10892. _sapp.glx.GetFBConfigAttrib(_sapp.x11.display, fbconfig, attrib, &value);
  10893. return value;
  10894. }
  10895. _SOKOL_PRIVATE GLXFBConfig _sapp_glx_choosefbconfig(void) {
  10896. GLXFBConfig* native_configs;
  10897. _sapp_gl_fbconfig* usable_configs;
  10898. const _sapp_gl_fbconfig* closest;
  10899. int i, native_count, usable_count;
  10900. const char* vendor;
  10901. bool trust_window_bit = true;
  10902. /* HACK: This is a (hopefully temporary) workaround for Chromium
  10903. (VirtualBox GL) not setting the window bit on any GLXFBConfigs
  10904. */
  10905. vendor = _sapp.glx.GetClientString(_sapp.x11.display, GLX_VENDOR);
  10906. if (vendor && strcmp(vendor, "Chromium") == 0) {
  10907. trust_window_bit = false;
  10908. }
  10909. native_configs = _sapp.glx.GetFBConfigs(_sapp.x11.display, _sapp.x11.screen, &native_count);
  10910. if (!native_configs || !native_count) {
  10911. _SAPP_PANIC(LINUX_GLX_NO_GLXFBCONFIGS);
  10912. }
  10913. usable_configs = (_sapp_gl_fbconfig*) _sapp_malloc_clear((size_t)native_count * sizeof(_sapp_gl_fbconfig));
  10914. usable_count = 0;
  10915. for (i = 0; i < native_count; i++) {
  10916. const GLXFBConfig n = native_configs[i];
  10917. _sapp_gl_fbconfig* u = usable_configs + usable_count;
  10918. _sapp_gl_init_fbconfig(u);
  10919. /* Only consider RGBA GLXFBConfigs */
  10920. if (0 == (_sapp_glx_attrib(n, GLX_RENDER_TYPE) & GLX_RGBA_BIT)) {
  10921. continue;
  10922. }
  10923. /* Only consider window GLXFBConfigs */
  10924. if (0 == (_sapp_glx_attrib(n, GLX_DRAWABLE_TYPE) & GLX_WINDOW_BIT)) {
  10925. if (trust_window_bit) {
  10926. continue;
  10927. }
  10928. }
  10929. u->red_bits = _sapp_glx_attrib(n, GLX_RED_SIZE);
  10930. u->green_bits = _sapp_glx_attrib(n, GLX_GREEN_SIZE);
  10931. u->blue_bits = _sapp_glx_attrib(n, GLX_BLUE_SIZE);
  10932. u->alpha_bits = _sapp_glx_attrib(n, GLX_ALPHA_SIZE);
  10933. u->depth_bits = _sapp_glx_attrib(n, GLX_DEPTH_SIZE);
  10934. u->stencil_bits = _sapp_glx_attrib(n, GLX_STENCIL_SIZE);
  10935. if (_sapp_glx_attrib(n, GLX_DOUBLEBUFFER)) {
  10936. u->doublebuffer = true;
  10937. }
  10938. if (_sapp.glx.ARB_multisample) {
  10939. u->samples = _sapp_glx_attrib(n, GLX_SAMPLES);
  10940. }
  10941. u->handle = (uintptr_t) n;
  10942. usable_count++;
  10943. }
  10944. _sapp_gl_fbconfig desired;
  10945. _sapp_gl_init_fbconfig(&desired);
  10946. desired.red_bits = 8;
  10947. desired.green_bits = 8;
  10948. desired.blue_bits = 8;
  10949. desired.alpha_bits = 8;
  10950. desired.depth_bits = 24;
  10951. desired.stencil_bits = 8;
  10952. desired.doublebuffer = true;
  10953. desired.samples = _sapp.sample_count > 1 ? _sapp.sample_count : 0;
  10954. closest = _sapp_gl_choose_fbconfig(&desired, usable_configs, usable_count);
  10955. GLXFBConfig result = 0;
  10956. if (closest) {
  10957. result = (GLXFBConfig) closest->handle;
  10958. }
  10959. XFree(native_configs);
  10960. _sapp_free(usable_configs);
  10961. return result;
  10962. }
  10963. _SOKOL_PRIVATE void _sapp_glx_choose_visual(Visual** visual, int* depth) {
  10964. GLXFBConfig native = _sapp_glx_choosefbconfig();
  10965. if (0 == native) {
  10966. _SAPP_PANIC(LINUX_GLX_NO_SUITABLE_GLXFBCONFIG);
  10967. }
  10968. XVisualInfo* result = _sapp.glx.GetVisualFromFBConfig(_sapp.x11.display, native);
  10969. if (!result) {
  10970. _SAPP_PANIC(LINUX_GLX_GET_VISUAL_FROM_FBCONFIG_FAILED);
  10971. }
  10972. *visual = result->visual;
  10973. *depth = result->depth;
  10974. XFree(result);
  10975. }
  10976. _SOKOL_PRIVATE void _sapp_glx_make_current(void) {
  10977. _sapp.glx.MakeCurrent(_sapp.x11.display, _sapp.glx.window, _sapp.glx.ctx);
  10978. glGetIntegerv(GL_FRAMEBUFFER_BINDING, (GLint*)&_sapp.gl.framebuffer);
  10979. }
  10980. _SOKOL_PRIVATE void _sapp_glx_create_context(void) {
  10981. GLXFBConfig native = _sapp_glx_choosefbconfig();
  10982. if (0 == native){
  10983. _SAPP_PANIC(LINUX_GLX_NO_SUITABLE_GLXFBCONFIG);
  10984. }
  10985. if (!(_sapp.glx.ARB_create_context && _sapp.glx.ARB_create_context_profile)) {
  10986. _SAPP_PANIC(LINUX_GLX_REQUIRED_EXTENSIONS_MISSING);
  10987. }
  10988. _sapp_x11_grab_error_handler();
  10989. const int attribs[] = {
  10990. GLX_CONTEXT_MAJOR_VERSION_ARB, _sapp.desc.gl.major_version,
  10991. GLX_CONTEXT_MINOR_VERSION_ARB, _sapp.desc.gl.minor_version,
  10992. GLX_CONTEXT_PROFILE_MASK_ARB, GLX_CONTEXT_CORE_PROFILE_BIT_ARB,
  10993. GLX_CONTEXT_FLAGS_ARB, GLX_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB,
  10994. 0, 0
  10995. };
  10996. _sapp.glx.ctx = _sapp.glx.CreateContextAttribsARB(_sapp.x11.display, native, NULL, True, attribs);
  10997. if (!_sapp.glx.ctx) {
  10998. _SAPP_PANIC(LINUX_GLX_CREATE_CONTEXT_FAILED);
  10999. }
  11000. _sapp_x11_release_error_handler();
  11001. _sapp.glx.window = _sapp.glx.CreateWindow(_sapp.x11.display, native, _sapp.x11.window, NULL);
  11002. if (!_sapp.glx.window) {
  11003. _SAPP_PANIC(LINUX_GLX_CREATE_WINDOW_FAILED);
  11004. }
  11005. _sapp_glx_make_current();
  11006. }
  11007. _SOKOL_PRIVATE void _sapp_glx_destroy_context(void) {
  11008. if (_sapp.glx.window) {
  11009. _sapp.glx.DestroyWindow(_sapp.x11.display, _sapp.glx.window);
  11010. _sapp.glx.window = 0;
  11011. }
  11012. if (_sapp.glx.ctx) {
  11013. _sapp.glx.DestroyContext(_sapp.x11.display, _sapp.glx.ctx);
  11014. _sapp.glx.ctx = 0;
  11015. }
  11016. }
  11017. _SOKOL_PRIVATE void _sapp_glx_swap_buffers(void) {
  11018. _sapp.glx.SwapBuffers(_sapp.x11.display, _sapp.glx.window);
  11019. }
  11020. _SOKOL_PRIVATE void _sapp_glx_swapinterval(int interval) {
  11021. if (_sapp.glx.EXT_swap_control) {
  11022. _sapp.glx.SwapIntervalEXT(_sapp.x11.display, _sapp.glx.window, interval);
  11023. } else if (_sapp.glx.MESA_swap_control) {
  11024. _sapp.glx.SwapIntervalMESA(interval);
  11025. }
  11026. }
  11027. #endif // _SAPP_GLX
  11028. _SOKOL_PRIVATE void _sapp_x11_send_event(Atom type, int a, int b, int c, int d, int e) {
  11029. _SAPP_STRUCT(XEvent, event);
  11030. event.type = ClientMessage;
  11031. event.xclient.window = _sapp.x11.window;
  11032. event.xclient.format = 32;
  11033. event.xclient.message_type = type;
  11034. event.xclient.data.l[0] = a;
  11035. event.xclient.data.l[1] = b;
  11036. event.xclient.data.l[2] = c;
  11037. event.xclient.data.l[3] = d;
  11038. event.xclient.data.l[4] = e;
  11039. XSendEvent(_sapp.x11.display, _sapp.x11.root,
  11040. False,
  11041. SubstructureNotifyMask | SubstructureRedirectMask,
  11042. &event);
  11043. }
  11044. _SOKOL_PRIVATE bool _sapp_x11_wait_for_event(int event_type, double timeout_sec, XEvent* out_event) {
  11045. _sapp_timestamp_t ts;
  11046. _sapp_timestamp_init(&ts);
  11047. while (!XCheckTypedWindowEvent(_sapp.x11.display, _sapp.x11.window, event_type, out_event)) {
  11048. struct pollfd fd = { ConnectionNumber(_sapp.x11.display), POLLIN, 0 };
  11049. poll(&fd, 1, timeout_sec * 1000);
  11050. if (_sapp_timestamp_now(&ts) > timeout_sec) {
  11051. return false;
  11052. }
  11053. }
  11054. return true;
  11055. }
  11056. _SOKOL_PRIVATE void _sapp_x11_app_event(sapp_event_type type) {
  11057. if (_sapp_events_enabled()) {
  11058. _sapp_init_event(type);
  11059. _sapp_call_event(&_sapp.event);
  11060. }
  11061. }
  11062. _SOKOL_PRIVATE void _sapp_x11_update_dimensions(int x11_window_width, int x11_window_height) {
  11063. // NOTE: do *NOT* use _sapp.dpi_scale for the window scale
  11064. const float window_scale = _sapp.x11.dpi / 96.0f;
  11065. _sapp.window_width = _sapp_roundf_gzero(x11_window_width / window_scale);
  11066. _sapp.window_height = _sapp_roundf_gzero(x11_window_height / window_scale);
  11067. // NOTE: on Vulkan, updating the framebuffer dimensions is entirely handled
  11068. // by the swapchain management code
  11069. #if !defined(SOKOL_VULKAN)
  11070. int cur_fb_width = _sapp.framebuffer_width;
  11071. int cur_fb_height = _sapp.framebuffer_height;
  11072. _sapp.framebuffer_width = _sapp_roundf_gzero(_sapp.window_width * _sapp.dpi_scale);
  11073. _sapp.framebuffer_height = _sapp_roundf_gzero(_sapp.window_height * _sapp.dpi_scale);
  11074. bool dim_changed = (_sapp.framebuffer_width != cur_fb_width) || (_sapp.framebuffer_height != cur_fb_height);
  11075. if (dim_changed) {
  11076. #if defined(SOKOL_WGPU)
  11077. _sapp_wgpu_swapchain_size_changed();
  11078. #endif
  11079. if (!_sapp.first_frame) {
  11080. _sapp_x11_app_event(SAPP_EVENTTYPE_RESIZED);
  11081. }
  11082. }
  11083. #endif
  11084. }
  11085. _SOKOL_PRIVATE void _sapp_x11_update_dimensions_from_window_size(void) {
  11086. XWindowAttributes attribs;
  11087. XGetWindowAttributes(_sapp.x11.display, _sapp.x11.window, &attribs);
  11088. _sapp_x11_update_dimensions(attribs.width, attribs.height);
  11089. }
  11090. _SOKOL_PRIVATE void _sapp_x11_set_fullscreen(bool enable) {
  11091. /* NOTE: this function must be called after XMapWindow (which happens in _sapp_x11_show_window()) */
  11092. if (_sapp.x11.NET_WM_STATE && _sapp.x11.NET_WM_STATE_FULLSCREEN) {
  11093. if (enable) {
  11094. const int _NET_WM_STATE_ADD = 1;
  11095. _sapp_x11_send_event(_sapp.x11.NET_WM_STATE,
  11096. _NET_WM_STATE_ADD,
  11097. _sapp.x11.NET_WM_STATE_FULLSCREEN,
  11098. 0, 1, 0);
  11099. } else {
  11100. const int _NET_WM_STATE_REMOVE = 0;
  11101. _sapp_x11_send_event(_sapp.x11.NET_WM_STATE,
  11102. _NET_WM_STATE_REMOVE,
  11103. _sapp.x11.NET_WM_STATE_FULLSCREEN,
  11104. 0, 1, 0);
  11105. }
  11106. }
  11107. XFlush(_sapp.x11.display);
  11108. }
  11109. _SOKOL_PRIVATE void _sapp_x11_create_hidden_cursor(void) {
  11110. SOKOL_ASSERT(0 == _sapp.x11.hidden_cursor);
  11111. const int w = 16;
  11112. const int h = 16;
  11113. XcursorImage* img = XcursorImageCreate(w, h);
  11114. SOKOL_ASSERT(img && (img->width == 16) && (img->height == 16) && img->pixels);
  11115. img->xhot = 0;
  11116. img->yhot = 0;
  11117. const size_t num_bytes = (size_t)(w * h) * sizeof(XcursorPixel);
  11118. _sapp_clear(img->pixels, num_bytes);
  11119. _sapp.x11.hidden_cursor = XcursorImageLoadCursor(_sapp.x11.display, img);
  11120. XcursorImageDestroy(img);
  11121. }
  11122. _SOKOL_PRIVATE void _sapp_x11_create_standard_cursor(sapp_mouse_cursor cursor, const char* name, const char* theme, int size, uint32_t fallback_native) {
  11123. SOKOL_ASSERT((cursor >= 0) && (cursor < _SAPP_MOUSECURSOR_NUM));
  11124. SOKOL_ASSERT(_sapp.x11.display);
  11125. if (theme) {
  11126. XcursorImage* img = XcursorLibraryLoadImage(name, theme, size);
  11127. if (img) {
  11128. _sapp.x11.standard_cursors[cursor] = XcursorImageLoadCursor(_sapp.x11.display, img);
  11129. XcursorImageDestroy(img);
  11130. }
  11131. }
  11132. if (0 == _sapp.x11.standard_cursors[cursor]) {
  11133. _sapp.x11.standard_cursors[cursor] = XCreateFontCursor(_sapp.x11.display, fallback_native);
  11134. }
  11135. }
  11136. _SOKOL_PRIVATE void _sapp_x11_create_standard_cursors(void) {
  11137. SOKOL_ASSERT(_sapp.x11.display);
  11138. const char* cursor_theme = XcursorGetTheme(_sapp.x11.display);
  11139. const int size = XcursorGetDefaultSize(_sapp.x11.display);
  11140. _sapp_x11_create_standard_cursor(SAPP_MOUSECURSOR_ARROW, "default", cursor_theme, size, XC_left_ptr);
  11141. _sapp_x11_create_standard_cursor(SAPP_MOUSECURSOR_IBEAM, "text", cursor_theme, size, XC_xterm);
  11142. _sapp_x11_create_standard_cursor(SAPP_MOUSECURSOR_CROSSHAIR, "crosshair", cursor_theme, size, XC_crosshair);
  11143. _sapp_x11_create_standard_cursor(SAPP_MOUSECURSOR_POINTING_HAND, "pointer", cursor_theme, size, XC_hand2);
  11144. _sapp_x11_create_standard_cursor(SAPP_MOUSECURSOR_RESIZE_EW, "ew-resize", cursor_theme, size, XC_sb_h_double_arrow);
  11145. _sapp_x11_create_standard_cursor(SAPP_MOUSECURSOR_RESIZE_NS, "ns-resize", cursor_theme, size, XC_sb_v_double_arrow);
  11146. _sapp_x11_create_standard_cursor(SAPP_MOUSECURSOR_RESIZE_NWSE, "nwse-resize", cursor_theme, size, 0);
  11147. _sapp_x11_create_standard_cursor(SAPP_MOUSECURSOR_RESIZE_NESW, "nesw-resize", cursor_theme, size, 0);
  11148. _sapp_x11_create_standard_cursor(SAPP_MOUSECURSOR_RESIZE_ALL, "all-scroll", cursor_theme, size, XC_fleur);
  11149. _sapp_x11_create_standard_cursor(SAPP_MOUSECURSOR_NOT_ALLOWED, "no-allowed", cursor_theme, size, 0);
  11150. _sapp_x11_create_hidden_cursor();
  11151. }
  11152. _SOKOL_PRIVATE void _sapp_x11_destroy_standard_cursors(void) {
  11153. SOKOL_ASSERT(_sapp.x11.display);
  11154. if (_sapp.x11.hidden_cursor) {
  11155. XFreeCursor(_sapp.x11.display, _sapp.x11.hidden_cursor);
  11156. _sapp.x11.hidden_cursor = 0;
  11157. }
  11158. for (int i = 0; i < _SAPP_MOUSECURSOR_NUM; i++) {
  11159. if (_sapp.x11.standard_cursors[i]) {
  11160. XFreeCursor(_sapp.x11.display, _sapp.x11.standard_cursors[i]);
  11161. _sapp.x11.standard_cursors[i] = 0;
  11162. }
  11163. }
  11164. }
  11165. _SOKOL_PRIVATE bool _sapp_x11_make_custom_mouse_cursor(sapp_mouse_cursor cursor, const sapp_image_desc* desc) {
  11166. SOKOL_ASSERT((cursor >= 0) && (cursor < _SAPP_MOUSECURSOR_NUM));
  11167. SOKOL_ASSERT(0 == _sapp.x11.custom_cursors[cursor]);
  11168. XcursorImage* img = XcursorImageCreate(desc->width, desc->height);
  11169. SOKOL_ASSERT(img && ((int) img->width == desc->width) && ((int) img->height == desc->height) && img->pixels);
  11170. img->xhot = (XcursorDim) desc->cursor_hotspot_x;
  11171. img->yhot = (XcursorDim) desc->cursor_hotspot_y;
  11172. const size_t dest_num_bytes = (size_t)(img->width * img->height) * sizeof(XcursorPixel);
  11173. SOKOL_ASSERT(dest_num_bytes == desc->pixels.size);
  11174. // Copy RGBA -> BGRA
  11175. for (size_t i = 0; i < dest_num_bytes; i += 4) {
  11176. ((uint8_t*) img->pixels)[i+0] = ((uint8_t*) desc->pixels.ptr)[i+2];
  11177. ((uint8_t*) img->pixels)[i+1] = ((uint8_t*) desc->pixels.ptr)[i+1];
  11178. ((uint8_t*) img->pixels)[i+2] = ((uint8_t*) desc->pixels.ptr)[i+0];
  11179. ((uint8_t*) img->pixels)[i+3] = ((uint8_t*) desc->pixels.ptr)[i+3];
  11180. }
  11181. _sapp.x11.custom_cursors[cursor] = XcursorImageLoadCursor(_sapp.x11.display, img);
  11182. XcursorImageDestroy(img);
  11183. return 0 != _sapp.x11.custom_cursors[cursor];
  11184. }
  11185. _SOKOL_PRIVATE void _sapp_x11_destroy_custom_mouse_cursor(sapp_mouse_cursor cursor) {
  11186. SOKOL_ASSERT((cursor >= 0) && (cursor < _SAPP_MOUSECURSOR_NUM));
  11187. Cursor xcursor = _sapp.x11.custom_cursors[cursor];
  11188. _sapp.x11.custom_cursors[cursor] = 0;
  11189. SOKOL_ASSERT(xcursor);
  11190. XFreeCursor(_sapp.x11.display, xcursor);
  11191. }
  11192. _SOKOL_PRIVATE void _sapp_x11_toggle_fullscreen(void) {
  11193. _sapp.fullscreen = !_sapp.fullscreen;
  11194. _sapp_x11_set_fullscreen(_sapp.fullscreen);
  11195. _sapp_x11_update_dimensions_from_window_size();
  11196. }
  11197. _SOKOL_PRIVATE void _sapp_x11_update_cursor(sapp_mouse_cursor cursor, bool shown) {
  11198. SOKOL_ASSERT((cursor >= 0) && (cursor < _SAPP_MOUSECURSOR_NUM));
  11199. if (shown) {
  11200. if (_sapp.custom_cursor_bound[cursor]) {
  11201. Cursor xcursor = _sapp.x11.custom_cursors[cursor];
  11202. SOKOL_ASSERT(0 != xcursor);
  11203. XDefineCursor(_sapp.x11.display, _sapp.x11.window, xcursor);
  11204. } else if (_sapp.x11.standard_cursors[cursor]) {
  11205. XDefineCursor(_sapp.x11.display, _sapp.x11.window, _sapp.x11.standard_cursors[cursor]);
  11206. } else {
  11207. XUndefineCursor(_sapp.x11.display, _sapp.x11.window);
  11208. }
  11209. } else {
  11210. XDefineCursor(_sapp.x11.display, _sapp.x11.window, _sapp.x11.hidden_cursor);
  11211. }
  11212. XFlush(_sapp.x11.display);
  11213. }
  11214. _SOKOL_PRIVATE void _sapp_x11_lock_mouse(bool lock) {
  11215. if (lock == _sapp.mouse.locked) {
  11216. return;
  11217. }
  11218. _sapp.mouse.dx = 0.0f;
  11219. _sapp.mouse.dy = 0.0f;
  11220. _sapp.mouse.locked = lock;
  11221. if (_sapp.mouse.locked) {
  11222. if (_sapp.x11.xi.available) {
  11223. XIEventMask em;
  11224. unsigned char mask[XIMaskLen(XI_RawMotion)] = { 0 }; // XIMaskLen is a macro
  11225. em.deviceid = XIAllMasterDevices;
  11226. em.mask_len = sizeof(mask);
  11227. em.mask = mask;
  11228. XISetMask(mask, XI_RawMotion);
  11229. XISelectEvents(_sapp.x11.display, _sapp.x11.root, &em, 1);
  11230. }
  11231. XGrabPointer(_sapp.x11.display, // display
  11232. _sapp.x11.window, // grab_window
  11233. True, // owner_events
  11234. ButtonPressMask | ButtonReleaseMask | PointerMotionMask, // event_mask
  11235. GrabModeAsync, // pointer_mode
  11236. GrabModeAsync, // keyboard_mode
  11237. _sapp.x11.window, // confine_to
  11238. _sapp.x11.hidden_cursor, // cursor
  11239. CurrentTime); // time
  11240. } else {
  11241. if (_sapp.x11.xi.available) {
  11242. XIEventMask em;
  11243. unsigned char mask[] = { 0 };
  11244. em.deviceid = XIAllMasterDevices;
  11245. em.mask_len = sizeof(mask);
  11246. em.mask = mask;
  11247. XISelectEvents(_sapp.x11.display, _sapp.x11.root, &em, 1);
  11248. }
  11249. XWarpPointer(_sapp.x11.display, None, _sapp.x11.window, 0, 0, 0, 0, (int) _sapp.mouse.x, _sapp.mouse.y);
  11250. XUngrabPointer(_sapp.x11.display, CurrentTime);
  11251. }
  11252. XFlush(_sapp.x11.display);
  11253. }
  11254. _SOKOL_PRIVATE void _sapp_x11_set_clipboard_string(const char* str) {
  11255. SOKOL_ASSERT(_sapp.clipboard.enabled && _sapp.clipboard.buffer);
  11256. if (strlen(str) >= (size_t)_sapp.clipboard.buf_size) {
  11257. _SAPP_ERROR(CLIPBOARD_STRING_TOO_BIG);
  11258. }
  11259. XSetSelectionOwner(_sapp.x11.display, _sapp.x11.CLIPBOARD, _sapp.x11.window, CurrentTime);
  11260. if (XGetSelectionOwner(_sapp.x11.display, _sapp.x11.CLIPBOARD) != _sapp.x11.window) {
  11261. _SAPP_ERROR(LINUX_X11_FAILED_TO_BECOME_OWNER_OF_CLIPBOARD);
  11262. }
  11263. }
  11264. _SOKOL_PRIVATE const char* _sapp_x11_get_clipboard_string(void) {
  11265. SOKOL_ASSERT(_sapp.clipboard.enabled && _sapp.clipboard.buffer);
  11266. Atom none = XInternAtom(_sapp.x11.display, "SAPP_SELECTION", False);
  11267. Atom incremental = XInternAtom(_sapp.x11.display, "INCR", False);
  11268. if (XGetSelectionOwner(_sapp.x11.display, _sapp.x11.CLIPBOARD) == _sapp.x11.window) {
  11269. // Instead of doing a large number of X round-trips just to put this
  11270. // string into a window property and then read it back, just return it
  11271. return _sapp.clipboard.buffer;
  11272. }
  11273. XConvertSelection(_sapp.x11.display,
  11274. _sapp.x11.CLIPBOARD,
  11275. _sapp.x11.UTF8_STRING,
  11276. none,
  11277. _sapp.x11.window,
  11278. CurrentTime);
  11279. XEvent event;
  11280. if (!_sapp_x11_wait_for_event(SelectionNotify, 0.1, &event)) {
  11281. return NULL;
  11282. }
  11283. if (event.xselection.property == None) {
  11284. return NULL;
  11285. }
  11286. char* data = NULL;
  11287. Atom actualType;
  11288. int actualFormat;
  11289. unsigned long itemCount, bytesAfter;
  11290. const bool ret = XGetWindowProperty(_sapp.x11.display,
  11291. event.xselection.requestor,
  11292. event.xselection.property,
  11293. 0,
  11294. LONG_MAX,
  11295. True,
  11296. _sapp.x11.UTF8_STRING,
  11297. &actualType,
  11298. &actualFormat,
  11299. &itemCount,
  11300. &bytesAfter,
  11301. (unsigned char**) &data);
  11302. if (ret != Success || data == NULL) {
  11303. if (data != NULL) {
  11304. XFree(data);
  11305. }
  11306. return NULL;
  11307. }
  11308. if ((actualType == incremental) || (itemCount >= (size_t)_sapp.clipboard.buf_size)) {
  11309. _SAPP_ERROR(CLIPBOARD_STRING_TOO_BIG);
  11310. XFree(data);
  11311. return NULL;
  11312. }
  11313. _sapp_strcpy(data, _sapp.clipboard.buffer, (size_t)_sapp.clipboard.buf_size);
  11314. XFree(data);
  11315. return _sapp.clipboard.buffer;
  11316. }
  11317. _SOKOL_PRIVATE void _sapp_x11_update_window_title(void) {
  11318. Xutf8SetWMProperties(_sapp.x11.display,
  11319. _sapp.x11.window,
  11320. _sapp.window_title, _sapp.window_title,
  11321. NULL, 0, NULL, NULL, NULL);
  11322. XChangeProperty(_sapp.x11.display, _sapp.x11.window,
  11323. _sapp.x11.NET_WM_NAME, _sapp.x11.UTF8_STRING, 8,
  11324. PropModeReplace,
  11325. (unsigned char*)_sapp.window_title,
  11326. strlen(_sapp.window_title));
  11327. XChangeProperty(_sapp.x11.display, _sapp.x11.window,
  11328. _sapp.x11.NET_WM_ICON_NAME, _sapp.x11.UTF8_STRING, 8,
  11329. PropModeReplace,
  11330. (unsigned char*)_sapp.window_title,
  11331. strlen(_sapp.window_title));
  11332. XFlush(_sapp.x11.display);
  11333. }
  11334. _SOKOL_PRIVATE void _sapp_x11_set_icon(const sapp_icon_desc* icon_desc, int num_images) {
  11335. SOKOL_ASSERT((num_images > 0) && (num_images <= SAPP_MAX_ICONIMAGES));
  11336. int long_count = 0;
  11337. for (int i = 0; i < num_images; i++) {
  11338. const sapp_image_desc* img_desc = &icon_desc->images[i];
  11339. long_count += 2 + (img_desc->width * img_desc->height);
  11340. }
  11341. long* icon_data = (long*) _sapp_malloc_clear((size_t)long_count * sizeof(long));
  11342. SOKOL_ASSERT(icon_data);
  11343. long* dst = icon_data;
  11344. for (int img_index = 0; img_index < num_images; img_index++) {
  11345. const sapp_image_desc* img_desc = &icon_desc->images[img_index];
  11346. const uint8_t* src = (const uint8_t*) img_desc->pixels.ptr;
  11347. *dst++ = img_desc->width;
  11348. *dst++ = img_desc->height;
  11349. const int num_pixels = img_desc->width * img_desc->height;
  11350. for (int pixel_index = 0; pixel_index < num_pixels; pixel_index++) {
  11351. *dst++ = ((long)(src[pixel_index * 4 + 0]) << 16) |
  11352. ((long)(src[pixel_index * 4 + 1]) << 8) |
  11353. ((long)(src[pixel_index * 4 + 2]) << 0) |
  11354. ((long)(src[pixel_index * 4 + 3]) << 24);
  11355. }
  11356. }
  11357. XChangeProperty(_sapp.x11.display, _sapp.x11.window,
  11358. _sapp.x11.NET_WM_ICON,
  11359. XA_CARDINAL, 32,
  11360. PropModeReplace,
  11361. (unsigned char*)icon_data,
  11362. long_count);
  11363. _sapp_free(icon_data);
  11364. XFlush(_sapp.x11.display);
  11365. }
  11366. _SOKOL_PRIVATE void _sapp_x11_create_window(Visual* visual_or_null, int depth) {
  11367. Visual* visual = visual_or_null;
  11368. if (0 == visual_or_null) {
  11369. visual = DefaultVisual(_sapp.x11.display, _sapp.x11.screen);
  11370. depth = DefaultDepth(_sapp.x11.display, _sapp.x11.screen);
  11371. }
  11372. _sapp.x11.colormap = XCreateColormap(_sapp.x11.display, _sapp.x11.root, visual, AllocNone);
  11373. _SAPP_STRUCT(XSetWindowAttributes, wa);
  11374. const uint32_t wamask = CWBorderPixel | CWColormap | CWEventMask;
  11375. wa.colormap = _sapp.x11.colormap;
  11376. wa.border_pixel = 0;
  11377. wa.event_mask = StructureNotifyMask | KeyPressMask | KeyReleaseMask |
  11378. PointerMotionMask | ButtonPressMask | ButtonReleaseMask |
  11379. ExposureMask | FocusChangeMask | VisibilityChangeMask |
  11380. EnterWindowMask | LeaveWindowMask | PropertyChangeMask;
  11381. int display_width = DisplayWidth(_sapp.x11.display, _sapp.x11.screen);
  11382. int display_height = DisplayHeight(_sapp.x11.display, _sapp.x11.screen);
  11383. // NOTE: do *NOT* use _sapp.dpi_scale for the size multiplicator!
  11384. const float window_scale = _sapp.x11.dpi / 96.0f;
  11385. int x11_window_width = _sapp_roundf_gzero(_sapp.window_width * window_scale);
  11386. int x11_window_height = _sapp_roundf_gzero(_sapp.window_height * window_scale);
  11387. if (0 == _sapp.window_width) {
  11388. x11_window_width = (display_width * 4) / 5;
  11389. }
  11390. if (0 == _sapp.window_height) {
  11391. x11_window_height = (display_height * 4) / 5;
  11392. }
  11393. _sapp_x11_grab_error_handler();
  11394. _sapp.x11.window = XCreateWindow(_sapp.x11.display,
  11395. _sapp.x11.root,
  11396. 0, 0,
  11397. (uint32_t)x11_window_width,
  11398. (uint32_t)x11_window_height,
  11399. 0, /* border width */
  11400. depth, /* color depth */
  11401. InputOutput,
  11402. visual,
  11403. wamask,
  11404. &wa);
  11405. _sapp_x11_release_error_handler();
  11406. if (!_sapp.x11.window) {
  11407. _SAPP_PANIC(LINUX_X11_CREATE_WINDOW_FAILED);
  11408. }
  11409. Atom protocols[] = {
  11410. _sapp.x11.WM_DELETE_WINDOW
  11411. };
  11412. XSetWMProtocols(_sapp.x11.display, _sapp.x11.window, protocols, 1);
  11413. // NOTE: PPosition and PSize are obsolete and ignored
  11414. XSizeHints* hints = XAllocSizeHints();
  11415. hints->flags = PWinGravity;
  11416. hints->win_gravity = CenterGravity;
  11417. XSetWMNormalHints(_sapp.x11.display, _sapp.x11.window, hints);
  11418. XFree(hints);
  11419. // announce support for drag'n'drop
  11420. if (_sapp.drop.enabled) {
  11421. const Atom version = _SAPP_X11_XDND_VERSION;
  11422. XChangeProperty(_sapp.x11.display, _sapp.x11.window, _sapp.x11.xdnd.XdndAware, XA_ATOM, 32, PropModeReplace, (unsigned char*) &version, 1);
  11423. }
  11424. _sapp_x11_update_window_title();
  11425. _sapp_x11_update_dimensions_from_window_size();
  11426. }
  11427. _SOKOL_PRIVATE void _sapp_x11_destroy_window(void) {
  11428. if (_sapp.x11.window) {
  11429. XUnmapWindow(_sapp.x11.display, _sapp.x11.window);
  11430. XDestroyWindow(_sapp.x11.display, _sapp.x11.window);
  11431. _sapp.x11.window = 0;
  11432. }
  11433. if (_sapp.x11.colormap) {
  11434. XFreeColormap(_sapp.x11.display, _sapp.x11.colormap);
  11435. _sapp.x11.colormap = 0;
  11436. }
  11437. XFlush(_sapp.x11.display);
  11438. }
  11439. _SOKOL_PRIVATE bool _sapp_x11_window_visible(void) {
  11440. XWindowAttributes wa;
  11441. XGetWindowAttributes(_sapp.x11.display, _sapp.x11.window, &wa);
  11442. return wa.map_state == IsViewable;
  11443. }
  11444. _SOKOL_PRIVATE void _sapp_x11_show_window(void) {
  11445. if (!_sapp_x11_window_visible()) {
  11446. XMapWindow(_sapp.x11.display, _sapp.x11.window);
  11447. XEvent dummy;
  11448. _sapp_x11_wait_for_event(VisibilityNotify, 0.1, &dummy);
  11449. XRaiseWindow(_sapp.x11.display, _sapp.x11.window);
  11450. XFlush(_sapp.x11.display);
  11451. }
  11452. }
  11453. _SOKOL_PRIVATE void _sapp_x11_hide_window(void) {
  11454. XUnmapWindow(_sapp.x11.display, _sapp.x11.window);
  11455. XFlush(_sapp.x11.display);
  11456. }
  11457. _SOKOL_PRIVATE unsigned long _sapp_x11_get_window_property(Window window, Atom property, Atom type, unsigned char** value) {
  11458. Atom actualType;
  11459. int actualFormat;
  11460. unsigned long itemCount, bytesAfter;
  11461. XGetWindowProperty(_sapp.x11.display,
  11462. window,
  11463. property,
  11464. 0,
  11465. LONG_MAX,
  11466. False,
  11467. type,
  11468. &actualType,
  11469. &actualFormat,
  11470. &itemCount,
  11471. &bytesAfter,
  11472. value);
  11473. return itemCount;
  11474. }
  11475. _SOKOL_PRIVATE int _sapp_x11_get_window_state(void) {
  11476. int result = WithdrawnState;
  11477. struct {
  11478. CARD32 state;
  11479. Window icon;
  11480. } *state = NULL;
  11481. if (_sapp_x11_get_window_property(_sapp.x11.window, _sapp.x11.WM_STATE, _sapp.x11.WM_STATE, (unsigned char**)&state) >= 2) {
  11482. result = (int)state->state;
  11483. }
  11484. if (state) {
  11485. XFree(state);
  11486. }
  11487. return result;
  11488. }
  11489. _SOKOL_PRIVATE uint32_t _sapp_x11_key_modifier_bit(sapp_keycode key) {
  11490. switch (key) {
  11491. case SAPP_KEYCODE_LEFT_SHIFT:
  11492. case SAPP_KEYCODE_RIGHT_SHIFT:
  11493. return SAPP_MODIFIER_SHIFT;
  11494. case SAPP_KEYCODE_LEFT_CONTROL:
  11495. case SAPP_KEYCODE_RIGHT_CONTROL:
  11496. return SAPP_MODIFIER_CTRL;
  11497. case SAPP_KEYCODE_LEFT_ALT:
  11498. case SAPP_KEYCODE_RIGHT_ALT:
  11499. return SAPP_MODIFIER_ALT;
  11500. case SAPP_KEYCODE_LEFT_SUPER:
  11501. case SAPP_KEYCODE_RIGHT_SUPER:
  11502. return SAPP_MODIFIER_SUPER;
  11503. default:
  11504. return 0;
  11505. }
  11506. }
  11507. _SOKOL_PRIVATE uint32_t _sapp_x11_button_modifier_bit(sapp_mousebutton btn) {
  11508. switch (btn) {
  11509. case SAPP_MOUSEBUTTON_LEFT: return SAPP_MODIFIER_LMB;
  11510. case SAPP_MOUSEBUTTON_RIGHT: return SAPP_MODIFIER_RMB;
  11511. case SAPP_MOUSEBUTTON_MIDDLE: return SAPP_MODIFIER_MMB;
  11512. default: return 0;
  11513. }
  11514. }
  11515. _SOKOL_PRIVATE uint32_t _sapp_x11_mods(uint32_t x11_mods) {
  11516. uint32_t mods = 0;
  11517. if (x11_mods & ShiftMask) {
  11518. mods |= SAPP_MODIFIER_SHIFT;
  11519. }
  11520. if (x11_mods & ControlMask) {
  11521. mods |= SAPP_MODIFIER_CTRL;
  11522. }
  11523. if (x11_mods & Mod1Mask) {
  11524. mods |= SAPP_MODIFIER_ALT;
  11525. }
  11526. if (x11_mods & Mod4Mask) {
  11527. mods |= SAPP_MODIFIER_SUPER;
  11528. }
  11529. if (x11_mods & Button1Mask) {
  11530. mods |= SAPP_MODIFIER_LMB;
  11531. }
  11532. if (x11_mods & Button2Mask) {
  11533. mods |= SAPP_MODIFIER_MMB;
  11534. }
  11535. if (x11_mods & Button3Mask) {
  11536. mods |= SAPP_MODIFIER_RMB;
  11537. }
  11538. return mods;
  11539. }
  11540. _SOKOL_PRIVATE sapp_mousebutton _sapp_x11_translate_button(const XEvent* event) {
  11541. switch (event->xbutton.button) {
  11542. case Button1: return SAPP_MOUSEBUTTON_LEFT;
  11543. case Button2: return SAPP_MOUSEBUTTON_MIDDLE;
  11544. case Button3: return SAPP_MOUSEBUTTON_RIGHT;
  11545. default: return SAPP_MOUSEBUTTON_INVALID;
  11546. }
  11547. }
  11548. _SOKOL_PRIVATE void _sapp_x11_mouse_update(int x, int y, bool clear_dxdy) {
  11549. if (!_sapp.mouse.locked) {
  11550. const float new_x = (float)x;
  11551. const float new_y = (float)y;
  11552. if (clear_dxdy) {
  11553. _sapp.mouse.dx = 0.0f;
  11554. _sapp.mouse.dy = 0.0f;
  11555. } else if (_sapp.mouse.pos_valid) {
  11556. _sapp.mouse.dx = new_x - _sapp.mouse.x;
  11557. _sapp.mouse.dy = new_y - _sapp.mouse.y;
  11558. }
  11559. _sapp.mouse.x = new_x;
  11560. _sapp.mouse.y = new_y;
  11561. _sapp.mouse.pos_valid = true;
  11562. }
  11563. }
  11564. _SOKOL_PRIVATE void _sapp_x11_mouse_event(sapp_event_type type, sapp_mousebutton btn, uint32_t mods) {
  11565. if (_sapp_events_enabled()) {
  11566. _sapp_init_event(type);
  11567. _sapp.event.mouse_button = btn;
  11568. _sapp.event.modifiers = mods;
  11569. _sapp_call_event(&_sapp.event);
  11570. }
  11571. }
  11572. _SOKOL_PRIVATE void _sapp_x11_scroll_event(float x, float y, uint32_t mods) {
  11573. if (_sapp_events_enabled()) {
  11574. _sapp_init_event(SAPP_EVENTTYPE_MOUSE_SCROLL);
  11575. _sapp.event.modifiers = mods;
  11576. _sapp.event.scroll_x = x;
  11577. _sapp.event.scroll_y = y;
  11578. _sapp_call_event(&_sapp.event);
  11579. }
  11580. }
  11581. _SOKOL_PRIVATE void _sapp_x11_key_event(sapp_event_type type, sapp_keycode key, bool repeat, uint32_t mods) {
  11582. if (_sapp_events_enabled()) {
  11583. _sapp_init_event(type);
  11584. _sapp.event.key_code = key;
  11585. _sapp.event.key_repeat = repeat;
  11586. _sapp.event.modifiers = mods;
  11587. _sapp_call_event(&_sapp.event);
  11588. /* check if a CLIPBOARD_PASTED event must be sent too */
  11589. if (_sapp.clipboard.enabled &&
  11590. (type == SAPP_EVENTTYPE_KEY_DOWN) &&
  11591. (_sapp.event.modifiers == SAPP_MODIFIER_CTRL) &&
  11592. (_sapp.event.key_code == SAPP_KEYCODE_V))
  11593. {
  11594. _sapp_init_event(SAPP_EVENTTYPE_CLIPBOARD_PASTED);
  11595. _sapp_call_event(&_sapp.event);
  11596. }
  11597. }
  11598. }
  11599. _SOKOL_PRIVATE void _sapp_x11_char_event(uint32_t chr, bool repeat, uint32_t mods) {
  11600. if (_sapp_events_enabled()) {
  11601. _sapp_init_event(SAPP_EVENTTYPE_CHAR);
  11602. _sapp.event.char_code = chr;
  11603. _sapp.event.key_repeat = repeat;
  11604. _sapp.event.modifiers = mods;
  11605. _sapp_call_event(&_sapp.event);
  11606. }
  11607. }
  11608. _SOKOL_PRIVATE sapp_keycode _sapp_x11_translate_key(int scancode) {
  11609. if ((scancode >= 0) && (scancode < _SAPP_X11_MAX_X11_KEYCODES)) {
  11610. return _sapp.keycodes[scancode];
  11611. } else {
  11612. return SAPP_KEYCODE_INVALID;
  11613. }
  11614. }
  11615. _SOKOL_PRIVATE int32_t _sapp_x11_keysym_to_unicode(KeySym keysym) {
  11616. int min = 0;
  11617. int max = sizeof(_sapp_x11_keysymtab) / sizeof(struct _sapp_x11_codepair) - 1;
  11618. int mid;
  11619. /* First check for Latin-1 characters (1:1 mapping) */
  11620. if ((keysym >= 0x0020 && keysym <= 0x007e) ||
  11621. (keysym >= 0x00a0 && keysym <= 0x00ff))
  11622. {
  11623. return keysym;
  11624. }
  11625. /* Also check for directly encoded 24-bit UCS characters */
  11626. if ((keysym & 0xff000000) == 0x01000000) {
  11627. return keysym & 0x00ffffff;
  11628. }
  11629. /* Binary search in table */
  11630. while (max >= min) {
  11631. mid = (min + max) / 2;
  11632. if (_sapp_x11_keysymtab[mid].keysym < keysym) {
  11633. min = mid + 1;
  11634. } else if (_sapp_x11_keysymtab[mid].keysym > keysym) {
  11635. max = mid - 1;
  11636. } else {
  11637. return _sapp_x11_keysymtab[mid].ucs;
  11638. }
  11639. }
  11640. /* No matching Unicode value found */
  11641. return -1;
  11642. }
  11643. _SOKOL_PRIVATE bool _sapp_x11_keypress_repeat(int keycode) {
  11644. bool repeat = false;
  11645. if ((keycode >= 0) && (keycode < _SAPP_X11_MAX_X11_KEYCODES)) {
  11646. repeat = _sapp.x11.key_repeat[keycode];
  11647. _sapp.x11.key_repeat[keycode] = true;
  11648. }
  11649. return repeat;
  11650. }
  11651. _SOKOL_PRIVATE void _sapp_x11_keyrelease_repeat(int keycode) {
  11652. if ((keycode >= 0) && (keycode < _SAPP_X11_MAX_X11_KEYCODES)) {
  11653. _sapp.x11.key_repeat[keycode] = false;
  11654. }
  11655. }
  11656. _SOKOL_PRIVATE bool _sapp_x11_parse_dropped_files_list(const char* src) {
  11657. SOKOL_ASSERT(src);
  11658. SOKOL_ASSERT(_sapp.drop.buffer);
  11659. _sapp_clear_drop_buffer();
  11660. _sapp.drop.num_files = 0;
  11661. /*
  11662. src is (potentially percent-encoded) string made of one or multiple paths
  11663. separated by \r\n, each path starting with 'file://'
  11664. */
  11665. bool err = false;
  11666. int src_count = 0;
  11667. char src_chr = 0;
  11668. char* dst_ptr = _sapp.drop.buffer;
  11669. const char* dst_end_ptr = dst_ptr + (_sapp.drop.max_path_length - 1); // room for terminating 0
  11670. while (0 != (src_chr = *src++)) {
  11671. src_count++;
  11672. char dst_chr = 0;
  11673. /* check leading 'file://' */
  11674. if (src_count <= 7) {
  11675. if (((src_count == 1) && (src_chr != 'f')) ||
  11676. ((src_count == 2) && (src_chr != 'i')) ||
  11677. ((src_count == 3) && (src_chr != 'l')) ||
  11678. ((src_count == 4) && (src_chr != 'e')) ||
  11679. ((src_count == 5) && (src_chr != ':')) ||
  11680. ((src_count == 6) && (src_chr != '/')) ||
  11681. ((src_count == 7) && (src_chr != '/')))
  11682. {
  11683. _SAPP_ERROR(LINUX_X11_DROPPED_FILE_URI_WRONG_SCHEME);
  11684. err = true;
  11685. break;
  11686. }
  11687. } else if (src_chr == '\r') {
  11688. // skip
  11689. } else if (src_chr == '\n') {
  11690. src_count = 0;
  11691. _sapp.drop.num_files++;
  11692. // too many files is not an error
  11693. if (_sapp.drop.num_files >= _sapp.drop.max_files) {
  11694. break;
  11695. }
  11696. dst_ptr = _sapp.drop.buffer + _sapp.drop.num_files * _sapp.drop.max_path_length;
  11697. dst_end_ptr = dst_ptr + (_sapp.drop.max_path_length - 1);
  11698. } else if ((src_chr == '%') && src[0] && src[1]) {
  11699. // a percent-encoded byte (most likely UTF-8 multibyte sequence)
  11700. const char digits[3] = { src[0], src[1], 0 };
  11701. src += 2;
  11702. dst_chr = (char) strtol(digits, 0, 16);
  11703. } else {
  11704. dst_chr = src_chr;
  11705. }
  11706. if (dst_chr) {
  11707. // dst_end_ptr already has adjustment for terminating zero
  11708. if (dst_ptr < dst_end_ptr) {
  11709. *dst_ptr++ = dst_chr;
  11710. } else {
  11711. _SAPP_ERROR(DROPPED_FILE_PATH_TOO_LONG);
  11712. err = true;
  11713. break;
  11714. }
  11715. }
  11716. }
  11717. if (err) {
  11718. _sapp_clear_drop_buffer();
  11719. _sapp.drop.num_files = 0;
  11720. return false;
  11721. } else {
  11722. return true;
  11723. }
  11724. }
  11725. _SOKOL_PRIVATE void _sapp_x11_on_genericevent(XEvent* event) {
  11726. if (_sapp.mouse.locked && _sapp.x11.xi.available) {
  11727. if (event->xcookie.extension == _sapp.x11.xi.major_opcode) {
  11728. if (XGetEventData(_sapp.x11.display, &event->xcookie)) {
  11729. if (event->xcookie.evtype == XI_RawMotion) {
  11730. XIRawEvent* re = (XIRawEvent*) event->xcookie.data;
  11731. if (re->valuators.mask_len) {
  11732. const double* values = re->raw_values;
  11733. if (XIMaskIsSet(re->valuators.mask, 0)) {
  11734. _sapp.mouse.dx = (float) *values;
  11735. values++;
  11736. }
  11737. if (XIMaskIsSet(re->valuators.mask, 1)) {
  11738. _sapp.mouse.dy = (float) *values;
  11739. }
  11740. _sapp_x11_mouse_event(SAPP_EVENTTYPE_MOUSE_MOVE, SAPP_MOUSEBUTTON_INVALID, _sapp_x11_mods(event->xmotion.state));
  11741. }
  11742. }
  11743. XFreeEventData(_sapp.x11.display, &event->xcookie);
  11744. }
  11745. }
  11746. }
  11747. }
  11748. _SOKOL_PRIVATE void _sapp_x11_on_focusin(XEvent* event) {
  11749. // NOTE: ignoring NotifyGrab and NotifyUngrab is same behaviour as GLFW
  11750. if ((event->xfocus.mode != NotifyGrab) && (event->xfocus.mode != NotifyUngrab)) {
  11751. _sapp_x11_app_event(SAPP_EVENTTYPE_FOCUSED);
  11752. }
  11753. }
  11754. _SOKOL_PRIVATE void _sapp_x11_on_focusout(XEvent* event) {
  11755. // if focus is lost for any reason, and we're in mouse locked mode, disable mouse lock
  11756. if (_sapp.mouse.locked) {
  11757. _sapp_x11_lock_mouse(false);
  11758. }
  11759. // NOTE: ignoring NotifyGrab and NotifyUngrab is same behaviour as GLFW
  11760. if ((event->xfocus.mode != NotifyGrab) && (event->xfocus.mode != NotifyUngrab)) {
  11761. _sapp_x11_app_event(SAPP_EVENTTYPE_UNFOCUSED);
  11762. }
  11763. }
  11764. _SOKOL_PRIVATE void _sapp_x11_on_keypress(XEvent* event) {
  11765. int keycode = (int)event->xkey.keycode;
  11766. const sapp_keycode key = _sapp_x11_translate_key(keycode);
  11767. const bool repeat = _sapp_x11_keypress_repeat(keycode);
  11768. uint32_t mods = _sapp_x11_mods(event->xkey.state);
  11769. // X11 doesn't set modifier bit on key down, so emulate that
  11770. mods |= _sapp_x11_key_modifier_bit(key);
  11771. if (key != SAPP_KEYCODE_INVALID) {
  11772. _sapp_x11_key_event(SAPP_EVENTTYPE_KEY_DOWN, key, repeat, mods);
  11773. }
  11774. KeySym keysym;
  11775. XLookupString(&event->xkey, NULL, 0, &keysym, NULL);
  11776. int32_t chr = _sapp_x11_keysym_to_unicode(keysym);
  11777. if (chr > 0) {
  11778. _sapp_x11_char_event((uint32_t)chr, repeat, mods);
  11779. }
  11780. }
  11781. _SOKOL_PRIVATE void _sapp_x11_on_keyrelease(XEvent* event) {
  11782. int keycode = (int)event->xkey.keycode;
  11783. const sapp_keycode key = _sapp_x11_translate_key(keycode);
  11784. _sapp_x11_keyrelease_repeat(keycode);
  11785. if (key != SAPP_KEYCODE_INVALID) {
  11786. uint32_t mods = _sapp_x11_mods(event->xkey.state);
  11787. // X11 doesn't clear modifier bit on key up, so emulate that
  11788. mods &= ~_sapp_x11_key_modifier_bit(key);
  11789. _sapp_x11_key_event(SAPP_EVENTTYPE_KEY_UP, key, false, mods);
  11790. }
  11791. }
  11792. _SOKOL_PRIVATE void _sapp_x11_on_buttonpress(XEvent* event) {
  11793. _sapp_x11_mouse_update(event->xbutton.x, event->xbutton.y, false);
  11794. const sapp_mousebutton btn = _sapp_x11_translate_button(event);
  11795. uint32_t mods = _sapp_x11_mods(event->xbutton.state);
  11796. // X11 doesn't set modifier bit on button down, so emulate that
  11797. mods |= _sapp_x11_button_modifier_bit(btn);
  11798. if (btn != SAPP_MOUSEBUTTON_INVALID) {
  11799. _sapp_x11_mouse_event(SAPP_EVENTTYPE_MOUSE_DOWN, btn, mods);
  11800. _sapp.x11.mouse_buttons |= (1 << btn);
  11801. } else {
  11802. // might be a scroll event
  11803. switch (event->xbutton.button) {
  11804. case 4: _sapp_x11_scroll_event(0.0f, 1.0f, mods); break;
  11805. case 5: _sapp_x11_scroll_event(0.0f, -1.0f, mods); break;
  11806. case 6: _sapp_x11_scroll_event(1.0f, 0.0f, mods); break;
  11807. case 7: _sapp_x11_scroll_event(-1.0f, 0.0f, mods); break;
  11808. }
  11809. }
  11810. }
  11811. _SOKOL_PRIVATE void _sapp_x11_on_buttonrelease(XEvent* event) {
  11812. _sapp_x11_mouse_update(event->xbutton.x, event->xbutton.y, false);
  11813. const sapp_mousebutton btn = _sapp_x11_translate_button(event);
  11814. if (btn != SAPP_MOUSEBUTTON_INVALID) {
  11815. uint32_t mods = _sapp_x11_mods(event->xbutton.state);
  11816. // X11 doesn't clear modifier bit on button up, so emulate that
  11817. mods &= ~_sapp_x11_button_modifier_bit(btn);
  11818. _sapp_x11_mouse_event(SAPP_EVENTTYPE_MOUSE_UP, btn, mods);
  11819. _sapp.x11.mouse_buttons &= ~(1 << btn);
  11820. }
  11821. }
  11822. _SOKOL_PRIVATE void _sapp_x11_on_enternotify(XEvent* event) {
  11823. // don't send enter/leave events while mouse button held down
  11824. if (0 == _sapp.x11.mouse_buttons) {
  11825. _sapp_x11_mouse_update(event->xcrossing.x, event->xcrossing.y, true);
  11826. _sapp_x11_mouse_event(SAPP_EVENTTYPE_MOUSE_ENTER, SAPP_MOUSEBUTTON_INVALID, _sapp_x11_mods(event->xcrossing.state));
  11827. }
  11828. }
  11829. _SOKOL_PRIVATE void _sapp_x11_on_leavenotify(XEvent* event) {
  11830. if (0 == _sapp.x11.mouse_buttons) {
  11831. _sapp_x11_mouse_update(event->xcrossing.x, event->xcrossing.y, true);
  11832. _sapp_x11_mouse_event(SAPP_EVENTTYPE_MOUSE_LEAVE, SAPP_MOUSEBUTTON_INVALID, _sapp_x11_mods(event->xcrossing.state));
  11833. }
  11834. }
  11835. _SOKOL_PRIVATE void _sapp_x11_on_motionnotify(XEvent* event) {
  11836. if (!_sapp.mouse.locked) {
  11837. _sapp_x11_mouse_update(event->xmotion.x, event->xmotion.y, false);
  11838. _sapp_x11_mouse_event(SAPP_EVENTTYPE_MOUSE_MOVE, SAPP_MOUSEBUTTON_INVALID, _sapp_x11_mods(event->xmotion.state));
  11839. }
  11840. }
  11841. _SOKOL_PRIVATE void _sapp_x11_on_propertynotify(XEvent* event) {
  11842. if (event->xproperty.state == PropertyNewValue) {
  11843. if (event->xproperty.atom == _sapp.x11.WM_STATE) {
  11844. const int state = _sapp_x11_get_window_state();
  11845. if (state != _sapp.x11.window_state) {
  11846. _sapp.x11.window_state = state;
  11847. if (state == IconicState) {
  11848. _sapp_x11_app_event(SAPP_EVENTTYPE_ICONIFIED);
  11849. } else if (state == NormalState) {
  11850. _sapp_x11_app_event(SAPP_EVENTTYPE_RESTORED);
  11851. }
  11852. }
  11853. }
  11854. }
  11855. }
  11856. _SOKOL_PRIVATE void _sapp_x11_on_selectionnotify(XEvent* event) {
  11857. if (event->xselection.property == _sapp.x11.xdnd.XdndSelection) {
  11858. char* data = 0;
  11859. uint32_t result = _sapp_x11_get_window_property(event->xselection.requestor,
  11860. event->xselection.property,
  11861. event->xselection.target,
  11862. (unsigned char**) &data);
  11863. if (_sapp.drop.enabled && result) {
  11864. if (_sapp_x11_parse_dropped_files_list(data)) {
  11865. _sapp.mouse.dx = 0.0f;
  11866. _sapp.mouse.dy = 0.0f;
  11867. if (_sapp_events_enabled()) {
  11868. // FIXME: Figure out how to get modifier key state here.
  11869. // The XSelection event has no 'state' item, and
  11870. // XQueryKeymap() always returns a zeroed array.
  11871. _sapp_init_event(SAPP_EVENTTYPE_FILES_DROPPED);
  11872. _sapp_call_event(&_sapp.event);
  11873. }
  11874. }
  11875. }
  11876. if (_sapp.x11.xdnd.version >= 2) {
  11877. _SAPP_STRUCT(XEvent, reply);
  11878. reply.type = ClientMessage;
  11879. reply.xclient.window = _sapp.x11.xdnd.source;
  11880. reply.xclient.message_type = _sapp.x11.xdnd.XdndFinished;
  11881. reply.xclient.format = 32;
  11882. reply.xclient.data.l[0] = (long)_sapp.x11.window;
  11883. reply.xclient.data.l[1] = result;
  11884. reply.xclient.data.l[2] = (long)_sapp.x11.xdnd.XdndActionCopy;
  11885. XSendEvent(_sapp.x11.display, _sapp.x11.xdnd.source, False, NoEventMask, &reply);
  11886. XFlush(_sapp.x11.display);
  11887. }
  11888. if (data) {
  11889. XFree(data);
  11890. }
  11891. }
  11892. }
  11893. _SOKOL_PRIVATE void _sapp_x11_on_clientmessage(XEvent* event) {
  11894. if (XFilterEvent(event, None)) {
  11895. return;
  11896. }
  11897. if (event->xclient.message_type == _sapp.x11.WM_PROTOCOLS) {
  11898. const Atom protocol = (Atom)event->xclient.data.l[0];
  11899. if (protocol == _sapp.x11.WM_DELETE_WINDOW) {
  11900. _sapp.quit_requested = true;
  11901. }
  11902. } else if (event->xclient.message_type == _sapp.x11.xdnd.XdndEnter) {
  11903. const bool is_list = 0 != (event->xclient.data.l[1] & 1);
  11904. _sapp.x11.xdnd.source = (Window)event->xclient.data.l[0];
  11905. _sapp.x11.xdnd.version = event->xclient.data.l[1] >> 24;
  11906. _sapp.x11.xdnd.format = None;
  11907. if (_sapp.x11.xdnd.version > _SAPP_X11_XDND_VERSION) {
  11908. return;
  11909. }
  11910. uint32_t count = 0;
  11911. Atom* formats = 0;
  11912. if (is_list) {
  11913. count = _sapp_x11_get_window_property(_sapp.x11.xdnd.source, _sapp.x11.xdnd.XdndTypeList, XA_ATOM, (unsigned char**)&formats);
  11914. } else {
  11915. count = 3;
  11916. formats = (Atom*) event->xclient.data.l + 2;
  11917. }
  11918. for (uint32_t i = 0; i < count; i++) {
  11919. if (formats[i] == _sapp.x11.xdnd.text_uri_list) {
  11920. _sapp.x11.xdnd.format = _sapp.x11.xdnd.text_uri_list;
  11921. break;
  11922. }
  11923. }
  11924. if (is_list && formats) {
  11925. XFree(formats);
  11926. }
  11927. } else if (event->xclient.message_type == _sapp.x11.xdnd.XdndDrop) {
  11928. if (_sapp.x11.xdnd.version > _SAPP_X11_XDND_VERSION) {
  11929. return;
  11930. }
  11931. Time time = CurrentTime;
  11932. if (_sapp.x11.xdnd.format) {
  11933. if (_sapp.x11.xdnd.version >= 1) {
  11934. time = (Time)event->xclient.data.l[2];
  11935. }
  11936. XConvertSelection(_sapp.x11.display,
  11937. _sapp.x11.xdnd.XdndSelection,
  11938. _sapp.x11.xdnd.format,
  11939. _sapp.x11.xdnd.XdndSelection,
  11940. _sapp.x11.window,
  11941. time);
  11942. } else if (_sapp.x11.xdnd.version >= 2) {
  11943. _SAPP_STRUCT(XEvent, reply);
  11944. reply.type = ClientMessage;
  11945. reply.xclient.window = _sapp.x11.xdnd.source;
  11946. reply.xclient.message_type = _sapp.x11.xdnd.XdndFinished;
  11947. reply.xclient.format = 32;
  11948. reply.xclient.data.l[0] = (long)_sapp.x11.window;
  11949. reply.xclient.data.l[1] = 0; // drag was rejected
  11950. reply.xclient.data.l[2] = None;
  11951. XSendEvent(_sapp.x11.display, _sapp.x11.xdnd.source, False, NoEventMask, &reply);
  11952. XFlush(_sapp.x11.display);
  11953. }
  11954. } else if (event->xclient.message_type == _sapp.x11.xdnd.XdndPosition) {
  11955. // drag operation has moved over the window
  11956. // FIXME: we could track the mouse position here, but
  11957. // this isn't implemented on other platforms either so far
  11958. if (_sapp.x11.xdnd.version > _SAPP_X11_XDND_VERSION) {
  11959. return;
  11960. }
  11961. _SAPP_STRUCT(XEvent, reply);
  11962. reply.type = ClientMessage;
  11963. reply.xclient.window = _sapp.x11.xdnd.source;
  11964. reply.xclient.message_type = _sapp.x11.xdnd.XdndStatus;
  11965. reply.xclient.format = 32;
  11966. reply.xclient.data.l[0] = (long)_sapp.x11.window;
  11967. if (_sapp.x11.xdnd.format) {
  11968. /* reply that we are ready to copy the dragged data */
  11969. reply.xclient.data.l[1] = 1; // accept with no rectangle
  11970. if (_sapp.x11.xdnd.version >= 2) {
  11971. reply.xclient.data.l[4] = (long)_sapp.x11.xdnd.XdndActionCopy;
  11972. }
  11973. }
  11974. XSendEvent(_sapp.x11.display, _sapp.x11.xdnd.source, False, NoEventMask, &reply);
  11975. XFlush(_sapp.x11.display);
  11976. }
  11977. }
  11978. _SOKOL_PRIVATE void _sapp_x11_on_selectionrequest(XEvent* event) {
  11979. XSelectionRequestEvent* req = &event->xselectionrequest;
  11980. if (req->selection != _sapp.x11.CLIPBOARD) {
  11981. return;
  11982. }
  11983. if (!_sapp.clipboard.enabled) {
  11984. return;
  11985. }
  11986. SOKOL_ASSERT(_sapp.clipboard.buffer);
  11987. _SAPP_STRUCT(XSelectionEvent, reply);
  11988. reply.type = SelectionNotify;
  11989. reply.display = req->display;
  11990. reply.requestor = req->requestor;
  11991. reply.selection = req->selection;
  11992. reply.target = req->target;
  11993. reply.property = req->property;
  11994. reply.time = req->time;
  11995. if (req->target == _sapp.x11.UTF8_STRING) {
  11996. XChangeProperty(_sapp.x11.display,
  11997. req->requestor,
  11998. req->property,
  11999. _sapp.x11.UTF8_STRING,
  12000. 8,
  12001. PropModeReplace,
  12002. (unsigned char*) _sapp.clipboard.buffer,
  12003. strlen(_sapp.clipboard.buffer));
  12004. } else if (req->target == _sapp.x11.TARGETS) {
  12005. XChangeProperty(_sapp.x11.display,
  12006. req->requestor,
  12007. req->property,
  12008. XA_ATOM,
  12009. 32,
  12010. PropModeReplace,
  12011. (unsigned char*) &_sapp.x11.UTF8_STRING,
  12012. 1);
  12013. } else {
  12014. reply.property = None;
  12015. }
  12016. XSendEvent(_sapp.x11.display, req->requestor, False, 0, (XEvent*) &reply);
  12017. }
  12018. _SOKOL_PRIVATE void _sapp_x11_process_event(XEvent* event) {
  12019. switch (event->type) {
  12020. case GenericEvent:
  12021. _sapp_x11_on_genericevent(event);
  12022. break;
  12023. case FocusIn:
  12024. _sapp_x11_on_focusin(event);
  12025. break;
  12026. case FocusOut:
  12027. _sapp_x11_on_focusout(event);
  12028. break;
  12029. case KeyPress:
  12030. _sapp_x11_on_keypress(event);
  12031. break;
  12032. case KeyRelease:
  12033. _sapp_x11_on_keyrelease(event);
  12034. break;
  12035. case ButtonPress:
  12036. _sapp_x11_on_buttonpress(event);
  12037. break;
  12038. case ButtonRelease:
  12039. _sapp_x11_on_buttonrelease(event);
  12040. break;
  12041. case EnterNotify:
  12042. _sapp_x11_on_enternotify(event);
  12043. break;
  12044. case LeaveNotify:
  12045. _sapp_x11_on_leavenotify(event);
  12046. break;
  12047. case MotionNotify:
  12048. _sapp_x11_on_motionnotify(event);
  12049. break;
  12050. case PropertyNotify:
  12051. _sapp_x11_on_propertynotify(event);
  12052. break;
  12053. case SelectionNotify:
  12054. _sapp_x11_on_selectionnotify(event);
  12055. break;
  12056. case SelectionRequest:
  12057. _sapp_x11_on_selectionrequest(event);
  12058. break;
  12059. case DestroyNotify:
  12060. // not a bug
  12061. break;
  12062. case ClientMessage:
  12063. _sapp_x11_on_clientmessage(event);
  12064. break;
  12065. }
  12066. }
  12067. #if defined(_SAPP_EGL)
  12068. _SOKOL_PRIVATE void _sapp_egl_init(void) {
  12069. #if defined(SOKOL_GLCORE)
  12070. if (!eglBindAPI(EGL_OPENGL_API)) {
  12071. _SAPP_PANIC(LINUX_EGL_BIND_OPENGL_API_FAILED);
  12072. }
  12073. #else
  12074. if (!eglBindAPI(EGL_OPENGL_ES_API)) {
  12075. _SAPP_PANIC(LINUX_EGL_BIND_OPENGL_ES_API_FAILED);
  12076. }
  12077. #endif
  12078. _sapp.egl.display = eglGetDisplay((EGLNativeDisplayType)_sapp.x11.display);
  12079. if (EGL_NO_DISPLAY == _sapp.egl.display) {
  12080. _SAPP_PANIC(LINUX_EGL_GET_DISPLAY_FAILED);
  12081. }
  12082. EGLint major, minor;
  12083. if (!eglInitialize(_sapp.egl.display, &major, &minor)) {
  12084. _SAPP_PANIC(LINUX_EGL_INITIALIZE_FAILED);
  12085. }
  12086. EGLint sample_count = _sapp.desc.sample_count > 1 ? _sapp.desc.sample_count : 0;
  12087. EGLint alpha_size = _sapp.desc.alpha ? 8 : 0;
  12088. const EGLint config_attrs[] = {
  12089. EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
  12090. #if defined(SOKOL_GLCORE)
  12091. EGL_RENDERABLE_TYPE, EGL_OPENGL_BIT,
  12092. #elif defined(SOKOL_GLES3)
  12093. EGL_RENDERABLE_TYPE, EGL_OPENGL_ES3_BIT,
  12094. #endif
  12095. EGL_RED_SIZE, 8,
  12096. EGL_GREEN_SIZE, 8,
  12097. EGL_BLUE_SIZE, 8,
  12098. EGL_ALPHA_SIZE, alpha_size,
  12099. EGL_DEPTH_SIZE, 24,
  12100. EGL_STENCIL_SIZE, 8,
  12101. EGL_SAMPLE_BUFFERS, _sapp.desc.sample_count > 1 ? 1 : 0,
  12102. EGL_SAMPLES, sample_count,
  12103. EGL_NONE,
  12104. };
  12105. EGLConfig egl_configs[32];
  12106. EGLint config_count;
  12107. if (!eglChooseConfig(_sapp.egl.display, config_attrs, egl_configs, 32, &config_count) || config_count == 0) {
  12108. _SAPP_PANIC(LINUX_EGL_NO_CONFIGS);
  12109. }
  12110. EGLConfig config = egl_configs[0];
  12111. for (int i = 0; i < config_count; ++i) {
  12112. EGLConfig c = egl_configs[i];
  12113. EGLint r, g, b, a, d, s, n;
  12114. if (eglGetConfigAttrib(_sapp.egl.display, c, EGL_RED_SIZE, &r) &&
  12115. eglGetConfigAttrib(_sapp.egl.display, c, EGL_GREEN_SIZE, &g) &&
  12116. eglGetConfigAttrib(_sapp.egl.display, c, EGL_BLUE_SIZE, &b) &&
  12117. eglGetConfigAttrib(_sapp.egl.display, c, EGL_ALPHA_SIZE, &a) &&
  12118. eglGetConfigAttrib(_sapp.egl.display, c, EGL_DEPTH_SIZE, &d) &&
  12119. eglGetConfigAttrib(_sapp.egl.display, c, EGL_STENCIL_SIZE, &s) &&
  12120. eglGetConfigAttrib(_sapp.egl.display, c, EGL_SAMPLES, &n) &&
  12121. (r == 8) && (g == 8) && (b == 8) && (a == alpha_size) && (d == 24) && (s == 8) && (n == sample_count)) {
  12122. config = c;
  12123. break;
  12124. }
  12125. }
  12126. EGLint visual_id;
  12127. if (!eglGetConfigAttrib(_sapp.egl.display, config, EGL_NATIVE_VISUAL_ID, &visual_id)) {
  12128. _SAPP_PANIC(LINUX_EGL_NO_NATIVE_VISUAL);
  12129. }
  12130. _SAPP_STRUCT(XVisualInfo, visual_info_template);
  12131. visual_info_template.visualid = (VisualID)visual_id;
  12132. int num_visuals;
  12133. XVisualInfo* visual_info = XGetVisualInfo(_sapp.x11.display, VisualIDMask, &visual_info_template, &num_visuals);
  12134. if (!visual_info) {
  12135. _SAPP_PANIC(LINUX_EGL_GET_VISUAL_INFO_FAILED);
  12136. }
  12137. _sapp_x11_create_window(visual_info->visual, visual_info->depth);
  12138. XFree(visual_info);
  12139. _sapp.egl.surface = eglCreateWindowSurface(_sapp.egl.display, config, (EGLNativeWindowType)_sapp.x11.window, NULL);
  12140. if (EGL_NO_SURFACE == _sapp.egl.surface) {
  12141. _SAPP_PANIC(LINUX_EGL_CREATE_WINDOW_SURFACE_FAILED);
  12142. }
  12143. EGLint ctx_attrs[] = {
  12144. EGL_CONTEXT_MAJOR_VERSION, _sapp.desc.gl.major_version,
  12145. EGL_CONTEXT_MINOR_VERSION, _sapp.desc.gl.minor_version,
  12146. #if defined(SOKOL_GLCORE)
  12147. EGL_CONTEXT_OPENGL_PROFILE_MASK, EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT,
  12148. #endif
  12149. EGL_NONE,
  12150. };
  12151. _sapp.egl.context = eglCreateContext(_sapp.egl.display, config, EGL_NO_CONTEXT, ctx_attrs);
  12152. if (EGL_NO_CONTEXT == _sapp.egl.context) {
  12153. _SAPP_PANIC(LINUX_EGL_CREATE_CONTEXT_FAILED);
  12154. }
  12155. if (!eglMakeCurrent(_sapp.egl.display, _sapp.egl.surface, _sapp.egl.surface, _sapp.egl.context)) {
  12156. _SAPP_PANIC(LINUX_EGL_MAKE_CURRENT_FAILED);
  12157. }
  12158. glGetIntegerv(GL_FRAMEBUFFER_BINDING, (GLint*)&_sapp.gl.framebuffer);
  12159. eglSwapInterval(_sapp.egl.display, _sapp.swap_interval);
  12160. }
  12161. _SOKOL_PRIVATE void _sapp_egl_destroy(void) {
  12162. if (_sapp.egl.display != EGL_NO_DISPLAY) {
  12163. eglMakeCurrent(_sapp.egl.display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
  12164. if (_sapp.egl.context != EGL_NO_CONTEXT) {
  12165. eglDestroyContext(_sapp.egl.display, _sapp.egl.context);
  12166. _sapp.egl.context = EGL_NO_CONTEXT;
  12167. }
  12168. if (_sapp.egl.surface != EGL_NO_SURFACE) {
  12169. eglDestroySurface(_sapp.egl.display, _sapp.egl.surface);
  12170. _sapp.egl.surface = EGL_NO_SURFACE;
  12171. }
  12172. eglTerminate(_sapp.egl.display);
  12173. _sapp.egl.display = EGL_NO_DISPLAY;
  12174. }
  12175. }
  12176. #endif // _SAPP_EGL
  12177. _SOKOL_PRIVATE void _sapp_linux_frame(void) {
  12178. _sapp_x11_update_dimensions_from_window_size();
  12179. #if defined(SOKOL_WGPU)
  12180. _sapp_wgpu_frame();
  12181. #elif defined(SOKOL_VULKAN)
  12182. _sapp_vk_frame();
  12183. #else
  12184. _sapp_frame();
  12185. #if defined(_SAPP_GLX)
  12186. _sapp_glx_swap_buffers();
  12187. #elif defined(_SAPP_EGL)
  12188. eglSwapBuffers(_sapp.egl.display, _sapp.egl.surface);
  12189. #endif
  12190. #endif
  12191. }
  12192. _SOKOL_PRIVATE void _sapp_linux_run(const sapp_desc* desc) {
  12193. /* The following lines are here to trigger a linker error instead of an
  12194. obscure runtime error if the user has forgotten to add -pthread to
  12195. the compiler or linker options. They have no other purpose.
  12196. */
  12197. pthread_attr_t pthread_attr;
  12198. pthread_attr_init(&pthread_attr);
  12199. pthread_attr_destroy(&pthread_attr);
  12200. _sapp_init_state(desc);
  12201. _sapp.x11.window_state = NormalState;
  12202. XInitThreads();
  12203. XrmInitialize();
  12204. _sapp.x11.display = XOpenDisplay(NULL);
  12205. if (!_sapp.x11.display) {
  12206. _SAPP_PANIC(LINUX_X11_OPEN_DISPLAY_FAILED);
  12207. }
  12208. _sapp.x11.screen = DefaultScreen(_sapp.x11.display);
  12209. _sapp.x11.root = DefaultRootWindow(_sapp.x11.display);
  12210. _sapp_x11_query_system_dpi();
  12211. // NOTE: on Linux system-window-size to frame-buffer-size mapping is always 1:1
  12212. _sapp.dpi_scale = _sapp.x11.dpi / 96.0f;
  12213. _sapp_x11_init_extensions();
  12214. _sapp_x11_create_standard_cursors();
  12215. XkbSetDetectableAutoRepeat(_sapp.x11.display, true, NULL);
  12216. _sapp_x11_init_keytable();
  12217. #if defined(_SAPP_GLX)
  12218. _sapp_glx_init();
  12219. Visual* visual = 0;
  12220. int depth = 0;
  12221. _sapp_glx_choose_visual(&visual, &depth);
  12222. _sapp_x11_create_window(visual, depth);
  12223. _sapp_glx_create_context();
  12224. _sapp_glx_swapinterval(_sapp.swap_interval);
  12225. #elif defined(_SAPP_EGL)
  12226. _sapp_egl_init();
  12227. #elif defined(SOKOL_WGPU)
  12228. _sapp_x11_create_window(0, 0);
  12229. _sapp_wgpu_init();
  12230. #elif defined(SOKOL_VULKAN)
  12231. _sapp_x11_create_window(0, 0);
  12232. _sapp_vk_init();
  12233. #endif
  12234. sapp_set_icon(&desc->icon);
  12235. _sapp.valid = true;
  12236. _sapp_x11_show_window();
  12237. if (_sapp.fullscreen) {
  12238. _sapp_x11_set_fullscreen(true);
  12239. }
  12240. XFlush(_sapp.x11.display);
  12241. while (!_sapp.quit_ordered) {
  12242. _sapp_timing_measure(&_sapp.timing);
  12243. int count = XPending(_sapp.x11.display);
  12244. while (count--) {
  12245. XEvent event;
  12246. XNextEvent(_sapp.x11.display, &event);
  12247. _sapp_x11_process_event(&event);
  12248. }
  12249. _sapp_linux_frame();
  12250. XFlush(_sapp.x11.display);
  12251. // handle quit-requested, either from window or from sapp_request_quit()
  12252. if (_sapp.quit_requested && !_sapp.quit_ordered) {
  12253. // give user code a chance to intervene
  12254. _sapp_x11_app_event(SAPP_EVENTTYPE_QUIT_REQUESTED);
  12255. /* if user code hasn't intervened, quit the app */
  12256. if (_sapp.quit_requested) {
  12257. _sapp.quit_ordered = true;
  12258. }
  12259. }
  12260. }
  12261. _sapp_call_cleanup();
  12262. #if defined(_SAPP_GLX)
  12263. _sapp_glx_destroy_context();
  12264. #elif defined(_SAPP_EGL)
  12265. _sapp_egl_destroy();
  12266. #elif defined(SOKOL_WGPU)
  12267. _sapp_wgpu_discard();
  12268. #elif defined(SOKOL_VULKAN)
  12269. _sapp_vk_discard();
  12270. #endif
  12271. _sapp_x11_destroy_window();
  12272. _sapp_x11_destroy_standard_cursors();
  12273. XCloseDisplay(_sapp.x11.display);
  12274. _sapp_discard_state();
  12275. }
  12276. #if !defined(SOKOL_NO_ENTRY)
  12277. int main(int argc, char* argv[]) {
  12278. sapp_desc desc = sokol_main(argc, argv);
  12279. _sapp_linux_run(&desc);
  12280. return 0;
  12281. }
  12282. #endif /* SOKOL_NO_ENTRY */
  12283. #endif /* _SAPP_LINUX */
  12284. // ██████ ██ ██ ██████ ██ ██ ██████
  12285. // ██ ██ ██ ██ ██ ██ ██ ██ ██
  12286. // ██████ ██ ██ ██████ ██ ██ ██
  12287. // ██ ██ ██ ██ ██ ██ ██ ██
  12288. // ██ ██████ ██████ ███████ ██ ██████
  12289. //
  12290. // >>public
  12291. #if defined(SOKOL_NO_ENTRY)
  12292. SOKOL_API_IMPL void sapp_run(const sapp_desc* desc) {
  12293. SOKOL_ASSERT(desc);
  12294. #if defined(_SAPP_MACOS)
  12295. _sapp_macos_run(desc);
  12296. #elif defined(_SAPP_IOS)
  12297. _sapp_ios_run(desc);
  12298. #elif defined(_SAPP_EMSCRIPTEN)
  12299. _sapp_emsc_run(desc);
  12300. #elif defined(_SAPP_WIN32)
  12301. _sapp_win32_run(desc);
  12302. #elif defined(_SAPP_LINUX)
  12303. _sapp_linux_run(desc);
  12304. #else
  12305. #error "sapp_run() not supported on this platform"
  12306. #endif
  12307. }
  12308. /* this is just a stub so the linker doesn't complain */
  12309. sapp_desc sokol_main(int argc, char* argv[]) {
  12310. _SOKOL_UNUSED(argc);
  12311. _SOKOL_UNUSED(argv);
  12312. _SAPP_STRUCT(sapp_desc, desc);
  12313. return desc;
  12314. }
  12315. #else
  12316. /* likewise, in normal mode, sapp_run() is just an empty stub */
  12317. SOKOL_API_IMPL void sapp_run(const sapp_desc* desc) {
  12318. _SOKOL_UNUSED(desc);
  12319. }
  12320. #endif
  12321. SOKOL_API_IMPL bool sapp_isvalid(void) {
  12322. return _sapp.valid;
  12323. }
  12324. SOKOL_API_IMPL void* sapp_userdata(void) {
  12325. return _sapp.desc.user_data;
  12326. }
  12327. SOKOL_API_IMPL sapp_desc sapp_query_desc(void) {
  12328. return _sapp.desc;
  12329. }
  12330. SOKOL_API_IMPL uint64_t sapp_frame_count(void) {
  12331. return _sapp.frame_count;
  12332. }
  12333. SOKOL_API_IMPL double sapp_frame_duration(void) {
  12334. return _sapp_timing_get_avg(&_sapp.timing);
  12335. }
  12336. SOKOL_API_IMPL int sapp_width(void) {
  12337. return (_sapp.framebuffer_width > 0) ? _sapp.framebuffer_width : 1;
  12338. }
  12339. SOKOL_API_IMPL float sapp_widthf(void) {
  12340. return (float)sapp_width();
  12341. }
  12342. SOKOL_API_IMPL int sapp_height(void) {
  12343. return (_sapp.framebuffer_height > 0) ? _sapp.framebuffer_height : 1;
  12344. }
  12345. SOKOL_API_IMPL float sapp_heightf(void) {
  12346. return (float)sapp_height();
  12347. }
  12348. SOKOL_API_IMPL sapp_pixel_format sapp_color_format(void) {
  12349. #if defined(SOKOL_WGPU)
  12350. switch (_sapp.wgpu.render_format) {
  12351. case WGPUTextureFormat_RGBA8Unorm:
  12352. return SAPP_PIXELFORMAT_RGBA8;
  12353. case WGPUTextureFormat_BGRA8Unorm:
  12354. return SAPP_PIXELFORMAT_BGRA8;
  12355. default:
  12356. SOKOL_UNREACHABLE;
  12357. return SAPP_PIXELFORMAT_NONE;
  12358. }
  12359. #elif defined(SOKOL_VULKAN)
  12360. switch (_sapp.vk.surface_format.format) {
  12361. case VK_FORMAT_R8G8B8A8_UNORM:
  12362. return SAPP_PIXELFORMAT_RGBA8;
  12363. case VK_FORMAT_B8G8R8A8_UNORM:
  12364. return SAPP_PIXELFORMAT_BGRA8;
  12365. default:
  12366. // FIXME!
  12367. SOKOL_UNREACHABLE;
  12368. return SAPP_PIXELFORMAT_NONE;
  12369. }
  12370. #elif defined(SOKOL_METAL) || defined(SOKOL_D3D11)
  12371. return SAPP_PIXELFORMAT_BGRA8;
  12372. #else
  12373. return SAPP_PIXELFORMAT_RGBA8;
  12374. #endif
  12375. }
  12376. SOKOL_API_IMPL sapp_pixel_format sapp_depth_format(void) {
  12377. return SAPP_PIXELFORMAT_DEPTH_STENCIL;
  12378. }
  12379. SOKOL_API_IMPL int sapp_sample_count(void) {
  12380. return _sapp.sample_count;
  12381. }
  12382. SOKOL_API_IMPL bool sapp_high_dpi(void) {
  12383. return _sapp.desc.high_dpi && (_sapp.dpi_scale >= 1.5f);
  12384. }
  12385. SOKOL_API_IMPL float sapp_dpi_scale(void) {
  12386. return _sapp.dpi_scale;
  12387. }
  12388. SOKOL_API_IMPL const void* sapp_egl_get_display(void) {
  12389. SOKOL_ASSERT(_sapp.valid);
  12390. #if defined(_SAPP_ANDROID)
  12391. return _sapp.android.display;
  12392. #elif defined(_SAPP_LINUX) && defined(_SAPP_EGL)
  12393. return _sapp.egl.display;
  12394. #else
  12395. return 0;
  12396. #endif
  12397. }
  12398. SOKOL_API_IMPL const void* sapp_egl_get_context(void) {
  12399. SOKOL_ASSERT(_sapp.valid);
  12400. #if defined(_SAPP_ANDROID)
  12401. return _sapp.android.context;
  12402. #elif defined(_SAPP_LINUX) && defined(_SAPP_EGL)
  12403. return _sapp.egl.context;
  12404. #else
  12405. return 0;
  12406. #endif
  12407. }
  12408. SOKOL_API_IMPL void sapp_show_keyboard(bool show) {
  12409. #if defined(_SAPP_IOS)
  12410. _sapp_ios_show_keyboard(show);
  12411. #elif defined(_SAPP_ANDROID)
  12412. _sapp_android_show_keyboard(show);
  12413. #else
  12414. _SOKOL_UNUSED(show);
  12415. #endif
  12416. }
  12417. SOKOL_API_IMPL bool sapp_keyboard_shown(void) {
  12418. return _sapp.onscreen_keyboard_shown;
  12419. }
  12420. SOKOL_API_IMPL bool sapp_is_fullscreen(void) {
  12421. return _sapp.fullscreen;
  12422. }
  12423. SOKOL_API_IMPL void sapp_toggle_fullscreen(void) {
  12424. #if defined(_SAPP_MACOS)
  12425. _sapp_macos_toggle_fullscreen();
  12426. #elif defined(_SAPP_WIN32)
  12427. _sapp_win32_toggle_fullscreen();
  12428. #elif defined(_SAPP_LINUX)
  12429. _sapp_x11_toggle_fullscreen();
  12430. #elif defined(_SAPP_EMSCRIPTEN)
  12431. _sapp_emsc_toggle_fullscreen();
  12432. #endif
  12433. }
  12434. _SOKOL_PRIVATE void _sapp_update_cursor(sapp_mouse_cursor cursor, bool shown) {
  12435. #if defined(_SAPP_MACOS)
  12436. _sapp_macos_update_cursor(cursor, shown);
  12437. #elif defined(_SAPP_WIN32)
  12438. _sapp_win32_update_cursor(cursor, shown, false);
  12439. #elif defined(_SAPP_LINUX)
  12440. _sapp_x11_update_cursor(cursor, shown);
  12441. #elif defined(_SAPP_EMSCRIPTEN)
  12442. _sapp_emsc_update_cursor(cursor, shown);
  12443. #endif
  12444. _sapp.mouse.current_cursor = cursor;
  12445. _sapp.mouse.shown = shown;
  12446. }
  12447. /* NOTE that sapp_show_mouse() does not "stack" like the Win32 or macOS API functions! */
  12448. SOKOL_API_IMPL void sapp_show_mouse(bool show) {
  12449. if (_sapp.mouse.shown != show) {
  12450. _sapp_update_cursor(_sapp.mouse.current_cursor, show);
  12451. }
  12452. }
  12453. SOKOL_API_IMPL bool sapp_mouse_shown(void) {
  12454. return _sapp.mouse.shown;
  12455. }
  12456. SOKOL_API_IMPL void sapp_lock_mouse(bool lock) {
  12457. #if defined(_SAPP_MACOS)
  12458. _sapp_macos_lock_mouse(lock);
  12459. #elif defined(_SAPP_EMSCRIPTEN)
  12460. _sapp_emsc_lock_mouse(lock);
  12461. #elif defined(_SAPP_WIN32)
  12462. _sapp_win32_lock_mouse(lock);
  12463. #elif defined(_SAPP_LINUX)
  12464. _sapp_x11_lock_mouse(lock);
  12465. #else
  12466. _sapp.mouse.locked = lock;
  12467. #endif
  12468. }
  12469. SOKOL_API_IMPL bool sapp_mouse_locked(void) {
  12470. return _sapp.mouse.locked;
  12471. }
  12472. SOKOL_API_IMPL void sapp_set_mouse_cursor(sapp_mouse_cursor cursor) {
  12473. SOKOL_ASSERT((cursor >= 0) && (cursor < _SAPP_MOUSECURSOR_NUM));
  12474. if (_sapp.mouse.current_cursor != cursor) {
  12475. _sapp_update_cursor(cursor, _sapp.mouse.shown);
  12476. }
  12477. }
  12478. SOKOL_API_IMPL sapp_mouse_cursor sapp_get_mouse_cursor(void) {
  12479. return _sapp.mouse.current_cursor;
  12480. }
  12481. SOKOL_API_IMPL sapp_mouse_cursor sapp_bind_mouse_cursor_image(sapp_mouse_cursor cursor, const sapp_image_desc* desc) {
  12482. SOKOL_ASSERT((cursor >= 0) && (cursor < _SAPP_MOUSECURSOR_NUM));
  12483. // NOTE: It seems that for some reason, the hotspot doesn't work if it is one less
  12484. // than the dimension of the cursor image (or more), on windows. So for a cursor
  12485. // that is 32 by 32 px, a hotspot of x = 30 works, but not x = 31.
  12486. // The cursor simply dissapears in such cases. Asserting for all platforms to make
  12487. // the behaviour consistent.
  12488. SOKOL_ASSERT(desc->cursor_hotspot_x < desc->width - 1 && desc->cursor_hotspot_y < desc->height - 1);
  12489. SOKOL_ASSERT(desc->width * desc->height * 4 == (int) desc->pixels.size);
  12490. sapp_unbind_mouse_cursor_image(cursor);
  12491. bool res = false;
  12492. #if defined(_SAPP_MACOS)
  12493. res = _sapp_macos_make_custom_mouse_cursor(cursor, desc);
  12494. #elif defined(_SAPP_EMSCRIPTEN)
  12495. res = _sapp_emsc_make_custom_mouse_cursor(cursor, desc);
  12496. #elif defined(_SAPP_WIN32)
  12497. res = _sapp_win32_make_custom_mouse_cursor(cursor, desc);
  12498. #elif defined(_SAPP_LINUX)
  12499. res = _sapp_x11_make_custom_mouse_cursor(cursor, desc);
  12500. #else
  12501. _SOKOL_UNUSED(desc);
  12502. #endif
  12503. _sapp.custom_cursor_bound[(int)cursor] = res;
  12504. // Update the displayed cursor in case the current cursor is the one we just bound.
  12505. if (_sapp.mouse.current_cursor == cursor) {
  12506. _sapp_update_cursor(cursor, _sapp.mouse.shown);
  12507. }
  12508. return cursor; // returning the passed-in cursor puerly for convenience, in case you want to asign the value to a variable.
  12509. }
  12510. SOKOL_APP_API_DECL void sapp_unbind_mouse_cursor_image(sapp_mouse_cursor cursor) {
  12511. SOKOL_ASSERT((cursor >= 0) && (cursor < _SAPP_MOUSECURSOR_NUM));
  12512. if (_sapp.custom_cursor_bound[(int)cursor]) {
  12513. // if this is the active cursor, first restore it to its default image,
  12514. // this must be done before attempting to destroy any cursor image
  12515. // resources which at least on win32 would fail if the cursor is still in use
  12516. _sapp.custom_cursor_bound[(int)cursor] = false;
  12517. if (_sapp.mouse.current_cursor == cursor) {
  12518. _sapp_update_cursor(cursor, _sapp.mouse.shown);
  12519. }
  12520. #if defined(_SAPP_MACOS)
  12521. _sapp_macos_destroy_custom_mouse_cursor(cursor);
  12522. #elif defined(_SAPP_EMSCRIPTEN)
  12523. _sapp_emsc_destroy_custom_mouse_cursor(cursor);
  12524. #elif defined(_SAPP_WIN32)
  12525. _sapp_win32_destroy_custom_mouse_cursor(cursor);
  12526. #elif defined(_SAPP_LINUX)
  12527. _sapp_x11_destroy_custom_mouse_cursor(cursor);
  12528. #endif
  12529. }
  12530. }
  12531. SOKOL_API_IMPL void sapp_request_quit(void) {
  12532. _sapp.quit_requested = true;
  12533. }
  12534. SOKOL_API_IMPL void sapp_cancel_quit(void) {
  12535. _sapp.quit_requested = false;
  12536. }
  12537. SOKOL_API_IMPL void sapp_quit(void) {
  12538. _sapp.quit_ordered = true;
  12539. }
  12540. SOKOL_API_IMPL void sapp_consume_event(void) {
  12541. _sapp.event_consumed = true;
  12542. }
  12543. /* NOTE: on HTML5, sapp_set_clipboard_string() must be called from within event handler! */
  12544. SOKOL_API_IMPL void sapp_set_clipboard_string(const char* str) {
  12545. if (!_sapp.clipboard.enabled) {
  12546. return;
  12547. }
  12548. SOKOL_ASSERT(str);
  12549. #if defined(_SAPP_MACOS)
  12550. _sapp_macos_set_clipboard_string(str);
  12551. #elif defined(_SAPP_EMSCRIPTEN)
  12552. _sapp_emsc_set_clipboard_string(str);
  12553. #elif defined(_SAPP_WIN32)
  12554. _sapp_win32_set_clipboard_string(str);
  12555. #elif defined(_SAPP_LINUX)
  12556. _sapp_x11_set_clipboard_string(str);
  12557. #else
  12558. /* not implemented */
  12559. #endif
  12560. _sapp_strcpy(str, _sapp.clipboard.buffer, (size_t)_sapp.clipboard.buf_size);
  12561. }
  12562. SOKOL_API_IMPL const char* sapp_get_clipboard_string(void) {
  12563. if (!_sapp.clipboard.enabled) {
  12564. return "";
  12565. }
  12566. #if defined(_SAPP_MACOS)
  12567. return _sapp_macos_get_clipboard_string();
  12568. #elif defined(_SAPP_EMSCRIPTEN)
  12569. return _sapp.clipboard.buffer;
  12570. #elif defined(_SAPP_WIN32)
  12571. return _sapp_win32_get_clipboard_string();
  12572. #elif defined(_SAPP_LINUX)
  12573. return _sapp_x11_get_clipboard_string();
  12574. #else
  12575. /* not implemented */
  12576. return _sapp.clipboard.buffer;
  12577. #endif
  12578. }
  12579. SOKOL_API_IMPL void sapp_set_window_title(const char* title) {
  12580. SOKOL_ASSERT(title);
  12581. _sapp_strcpy(title, _sapp.window_title, sizeof(_sapp.window_title));
  12582. #if defined(_SAPP_MACOS)
  12583. _sapp_macos_update_window_title();
  12584. #elif defined(_SAPP_WIN32)
  12585. _sapp_win32_update_window_title();
  12586. #elif defined(_SAPP_LINUX)
  12587. _sapp_x11_update_window_title();
  12588. #endif
  12589. }
  12590. SOKOL_API_IMPL void sapp_set_icon(const sapp_icon_desc* desc) {
  12591. SOKOL_ASSERT(desc);
  12592. if (desc->sokol_default) {
  12593. if (0 == _sapp.default_icon_pixels) {
  12594. _sapp_setup_default_icon();
  12595. }
  12596. SOKOL_ASSERT(0 != _sapp.default_icon_pixels);
  12597. desc = &_sapp.default_icon_desc;
  12598. }
  12599. const int num_images = _sapp_icon_num_images(desc);
  12600. if (num_images == 0) {
  12601. return;
  12602. }
  12603. SOKOL_ASSERT((num_images > 0) && (num_images <= SAPP_MAX_ICONIMAGES));
  12604. if (!_sapp_validate_icon_desc(desc, num_images)) {
  12605. return;
  12606. }
  12607. #if defined(_SAPP_MACOS)
  12608. _sapp_macos_set_icon(desc, num_images);
  12609. #elif defined(_SAPP_WIN32)
  12610. _sapp_win32_set_icon(desc, num_images);
  12611. #elif defined(_SAPP_LINUX)
  12612. _sapp_x11_set_icon(desc, num_images);
  12613. #elif defined(_SAPP_EMSCRIPTEN)
  12614. _sapp_emsc_set_icon(desc, num_images);
  12615. #endif
  12616. }
  12617. SOKOL_API_IMPL int sapp_get_num_dropped_files(void) {
  12618. SOKOL_ASSERT(_sapp.drop.enabled);
  12619. return _sapp.drop.num_files;
  12620. }
  12621. SOKOL_API_IMPL const char* sapp_get_dropped_file_path(int index) {
  12622. SOKOL_ASSERT(_sapp.drop.enabled);
  12623. SOKOL_ASSERT((index >= 0) && (index < _sapp.drop.num_files));
  12624. SOKOL_ASSERT(_sapp.drop.buffer);
  12625. if (!_sapp.drop.enabled) {
  12626. return "";
  12627. }
  12628. if ((index < 0) || (index >= _sapp.drop.max_files)) {
  12629. return "";
  12630. }
  12631. return (const char*) _sapp_dropped_file_path_ptr(index);
  12632. }
  12633. SOKOL_API_IMPL uint32_t sapp_html5_get_dropped_file_size(int index) {
  12634. SOKOL_ASSERT(_sapp.drop.enabled);
  12635. SOKOL_ASSERT((index >= 0) && (index < _sapp.drop.num_files));
  12636. #if defined(_SAPP_EMSCRIPTEN)
  12637. if (!_sapp.drop.enabled) {
  12638. return 0;
  12639. }
  12640. return sapp_js_dropped_file_size(index);
  12641. #else
  12642. (void)index;
  12643. return 0;
  12644. #endif
  12645. }
  12646. SOKOL_API_IMPL void sapp_html5_fetch_dropped_file(const sapp_html5_fetch_request* request) {
  12647. SOKOL_ASSERT(_sapp.drop.enabled);
  12648. SOKOL_ASSERT(request);
  12649. SOKOL_ASSERT(request->callback);
  12650. SOKOL_ASSERT(request->buffer.ptr);
  12651. SOKOL_ASSERT(request->buffer.size > 0);
  12652. #if defined(_SAPP_EMSCRIPTEN)
  12653. const int index = request->dropped_file_index;
  12654. sapp_html5_fetch_error error_code = SAPP_HTML5_FETCH_ERROR_NO_ERROR;
  12655. if ((index < 0) || (index >= _sapp.drop.num_files)) {
  12656. error_code = SAPP_HTML5_FETCH_ERROR_OTHER;
  12657. }
  12658. if (sapp_html5_get_dropped_file_size(index) > request->buffer.size) {
  12659. error_code = SAPP_HTML5_FETCH_ERROR_BUFFER_TOO_SMALL;
  12660. }
  12661. if (SAPP_HTML5_FETCH_ERROR_NO_ERROR != error_code) {
  12662. _sapp_emsc_invoke_fetch_cb(index,
  12663. false, // success
  12664. (int)error_code,
  12665. request->callback,
  12666. 0, // fetched_size
  12667. (void*)request->buffer.ptr,
  12668. request->buffer.size,
  12669. request->user_data);
  12670. } else {
  12671. sapp_js_fetch_dropped_file(index,
  12672. request->callback,
  12673. (void*)request->buffer.ptr,
  12674. request->buffer.size,
  12675. request->user_data);
  12676. }
  12677. #else
  12678. (void)request;
  12679. #endif
  12680. }
  12681. SOKOL_API_IMPL sapp_environment sapp_get_environment(void) {
  12682. SOKOL_ASSERT(_sapp.valid);
  12683. _SAPP_STRUCT(sapp_environment, res);
  12684. res.defaults.color_format = sapp_color_format();
  12685. res.defaults.depth_format = sapp_depth_format();
  12686. res.defaults.sample_count = sapp_sample_count();
  12687. #if defined(SOKOL_METAL)
  12688. #if defined(_SAPP_MACOS)
  12689. res.metal.device = (__bridge const void*) _sapp.macos.mtl_device;
  12690. #else
  12691. res.metal.device = (__bridge const void*) _sapp.ios.mtl_device;
  12692. #endif
  12693. #endif
  12694. #if defined(SOKOL_D3D11)
  12695. res.d3d11.device = (const void*) _sapp.d3d11.device;
  12696. res.d3d11.device_context = (const void*) _sapp.d3d11.device_context;
  12697. #endif
  12698. #if defined(SOKOL_WGPU)
  12699. res.wgpu.device = (const void*) _sapp.wgpu.device;
  12700. #endif
  12701. #if defined(SOKOL_VULKAN)
  12702. res.vulkan.physical_device = (const void*) _sapp.vk.physical_device;
  12703. res.vulkan.device = (const void*) _sapp.vk.device;
  12704. res.vulkan.queue = (const void*) _sapp.vk.queue;
  12705. res.vulkan.queue_family_index = _sapp.vk.queue_family_index;
  12706. #endif
  12707. return res;
  12708. }
  12709. SOKOL_API_IMPL sapp_swapchain sapp_get_swapchain(void) {
  12710. SOKOL_ASSERT(_sapp.valid);
  12711. _SAPP_STRUCT(sapp_swapchain, res);
  12712. res.width = sapp_width();
  12713. res.height = sapp_height();
  12714. res.color_format = sapp_color_format();
  12715. res.depth_format = sapp_depth_format();
  12716. res.sample_count = sapp_sample_count();
  12717. #if defined(SOKOL_METAL)
  12718. #if defined(_SAPP_MACOS)
  12719. res.metal.current_drawable = (__bridge const void*) [_sapp.macos.view currentDrawable];
  12720. res.metal.depth_stencil_texture = (__bridge const void*) [_sapp.macos.view depthStencilTexture];
  12721. res.metal.msaa_color_texture = (__bridge const void*) [_sapp.macos.view multisampleColorTexture];
  12722. #else
  12723. res.metal.current_drawable = (__bridge const void*) [_sapp.ios.view currentDrawable];
  12724. res.metal.depth_stencil_texture = (__bridge const void*) [_sapp.ios.view depthStencilTexture];
  12725. res.metal.msaa_color_texture = (__bridge const void*) [_sapp.ios.view multisampleColorTexture];
  12726. #endif
  12727. #endif
  12728. #if defined(SOKOL_D3D11)
  12729. SOKOL_ASSERT(_sapp.d3d11.rtv);
  12730. if (_sapp.sample_count > 1) {
  12731. SOKOL_ASSERT(_sapp.d3d11.msaa_rtv);
  12732. res.d3d11.render_view = (const void*) _sapp.d3d11.msaa_rtv;
  12733. res.d3d11.resolve_view = (const void*) _sapp.d3d11.rtv;
  12734. } else {
  12735. res.d3d11.render_view = (const void*) _sapp.d3d11.rtv;
  12736. }
  12737. res.d3d11.depth_stencil_view = (const void*) _sapp.d3d11.dsv;
  12738. #endif
  12739. #if defined(SOKOL_WGPU)
  12740. SOKOL_ASSERT(0 == _sapp.wgpu.swapchain_view);
  12741. _sapp_wgpu_swapchain_next();
  12742. // FIXME: swapchain_view being null must be allowed and should skip the frame
  12743. SOKOL_ASSERT(_sapp.wgpu.swapchain_view);
  12744. if (_sapp.sample_count > 1) {
  12745. SOKOL_ASSERT(_sapp.wgpu.msaa_view);
  12746. res.wgpu.render_view = (const void*) _sapp.wgpu.msaa_view;
  12747. res.wgpu.resolve_view = (const void*) _sapp.wgpu.swapchain_view;
  12748. } else {
  12749. res.wgpu.render_view = (const void*) _sapp.wgpu.swapchain_view;
  12750. }
  12751. res.wgpu.depth_stencil_view = (const void*) _sapp.wgpu.depth_stencil_view;
  12752. #endif
  12753. #if defined(SOKOL_VULKAN)
  12754. _sapp_vk_swapchain_next();
  12755. // FIXME: swapchain_view being null must be allowed and should skip the frame
  12756. uint32_t img_idx = _sapp.vk.cur_swapchain_image_index;
  12757. if (_sapp.sample_count > 1) {
  12758. SOKOL_ASSERT(_sapp.vk.msaa.img && _sapp.vk.msaa.view);
  12759. res.vulkan.render_image = (const void*) _sapp.vk.msaa.img;
  12760. res.vulkan.render_view = (const void*) _sapp.vk.msaa.view;
  12761. res.vulkan.resolve_image = (const void*) _sapp.vk.swapchain_images[img_idx];
  12762. res.vulkan.resolve_view = (const void*) _sapp.vk.swapchain_views[img_idx];
  12763. } else {
  12764. res.vulkan.render_image = (const void*) _sapp.vk.swapchain_images[img_idx];
  12765. res.vulkan.render_view = (const void*) _sapp.vk.swapchain_views[img_idx];
  12766. }
  12767. res.vulkan.depth_stencil_image = (const void*) _sapp.vk.depth.img;
  12768. res.vulkan.depth_stencil_view = (const void*) _sapp.vk.depth.view;
  12769. res.vulkan.render_finished_semaphore = _sapp.vk.sync[img_idx].render_finished_sem;
  12770. res.vulkan.present_complete_semaphore = _sapp.vk.sync[_sapp.vk.sync_slot].present_complete_sem;
  12771. #endif
  12772. #if defined(_SAPP_ANY_GL)
  12773. res.gl.framebuffer = _sapp.gl.framebuffer;
  12774. #endif
  12775. return res;
  12776. }
  12777. SOKOL_API_IMPL const void* sapp_macos_get_window(void) {
  12778. #if defined(_SAPP_MACOS)
  12779. const void* obj = (__bridge const void*) _sapp.macos.window;
  12780. SOKOL_ASSERT(obj);
  12781. return obj;
  12782. #else
  12783. return 0;
  12784. #endif
  12785. }
  12786. SOKOL_API_IMPL const void* sapp_ios_get_window(void) {
  12787. #if defined(_SAPP_IOS)
  12788. const void* obj = (__bridge const void*) _sapp.ios.window;
  12789. SOKOL_ASSERT(obj);
  12790. return obj;
  12791. #else
  12792. return 0;
  12793. #endif
  12794. }
  12795. SOKOL_API_IMPL const void* sapp_d3d11_get_swap_chain(void) {
  12796. SOKOL_ASSERT(_sapp.valid);
  12797. #if defined(SOKOL_D3D11)
  12798. return _sapp.d3d11.swap_chain;
  12799. #else
  12800. return 0;
  12801. #endif
  12802. }
  12803. SOKOL_API_IMPL const void* sapp_win32_get_hwnd(void) {
  12804. SOKOL_ASSERT(_sapp.valid);
  12805. #if defined(_SAPP_WIN32)
  12806. return _sapp.win32.hwnd;
  12807. #else
  12808. return 0;
  12809. #endif
  12810. }
  12811. SOKOL_API_IMPL int sapp_gl_get_major_version(void) {
  12812. SOKOL_ASSERT(_sapp.valid);
  12813. #if defined(_SAPP_ANY_GL)
  12814. return _sapp.desc.gl.major_version;
  12815. #else
  12816. return 0;
  12817. #endif
  12818. }
  12819. SOKOL_API_IMPL int sapp_gl_get_minor_version(void) {
  12820. SOKOL_ASSERT(_sapp.valid);
  12821. #if defined(_SAPP_ANY_GL)
  12822. return _sapp.desc.gl.minor_version;
  12823. #else
  12824. return 0;
  12825. #endif
  12826. }
  12827. SOKOL_API_IMPL bool sapp_gl_is_gles(void) {
  12828. #if defined(SOKOL_GLES3)
  12829. return true;
  12830. #else
  12831. return false;
  12832. #endif
  12833. }
  12834. SOKOL_API_IMPL const void* sapp_x11_get_window(void) {
  12835. #if defined(_SAPP_LINUX)
  12836. return (void*)_sapp.x11.window;
  12837. #else
  12838. return 0;
  12839. #endif
  12840. }
  12841. SOKOL_API_IMPL const void* sapp_x11_get_display(void) {
  12842. #if defined(_SAPP_LINUX)
  12843. return (void*)_sapp.x11.display;
  12844. #else
  12845. return 0;
  12846. #endif
  12847. }
  12848. SOKOL_API_IMPL const void* sapp_android_get_native_activity(void) {
  12849. // NOTE: _sapp.valid is not asserted here because sapp_android_get_native_activity()
  12850. // needs to be callable from within sokol_main() (see: https://github.com/floooh/sokol/issues/708)
  12851. #if defined(_SAPP_ANDROID)
  12852. return (void*)_sapp.android.activity;
  12853. #else
  12854. return 0;
  12855. #endif
  12856. }
  12857. SOKOL_API_IMPL void sapp_html5_ask_leave_site(bool ask) {
  12858. _sapp.html5_ask_leave_site = ask;
  12859. }
  12860. #endif /* SOKOL_APP_IMPL */