1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676367736783679368036813682368336843685368636873688368936903691369236933694369536963697369836993700370137023703370437053706370737083709371037113712371337143715371637173718371937203721372237233724372537263727372837293730373137323733373437353736373737383739374037413742374337443745374637473748374937503751375237533754375537563757375837593760376137623763376437653766376737683769377037713772377337743775377637773778377937803781378237833784378537863787378837893790379137923793379437953796379737983799380038013802380338043805380638073808380938103811381238133814381538163817381838193820382138223823382438253826382738283829383038313832383338343835383638373838383938403841384238433844384538463847384838493850385138523853385438553856385738583859386038613862386338643865386638673868386938703871387238733874387538763877387838793880388138823883388438853886388738883889389038913892389338943895389638973898389939003901390239033904390539063907390839093910391139123913391439153916391739183919392039213922392339243925392639273928392939303931393239333934393539363937393839393940394139423943394439453946394739483949395039513952395339543955395639573958395939603961396239633964396539663967396839693970397139723973397439753976397739783979398039813982398339843985398639873988398939903991399239933994399539963997399839994000400140024003400440054006400740084009401040114012401340144015401640174018401940204021402240234024402540264027402840294030403140324033403440354036403740384039404040414042404340444045404640474048404940504051405240534054405540564057405840594060406140624063406440654066406740684069407040714072407340744075407640774078407940804081408240834084408540864087408840894090409140924093409440954096409740984099410041014102410341044105410641074108410941104111411241134114411541164117411841194120412141224123412441254126412741284129413041314132413341344135413641374138413941404141414241434144414541464147414841494150415141524153415441554156415741584159416041614162416341644165416641674168416941704171417241734174417541764177417841794180418141824183418441854186418741884189419041914192419341944195419641974198419942004201420242034204420542064207420842094210421142124213421442154216421742184219422042214222422342244225422642274228422942304231423242334234423542364237423842394240424142424243424442454246424742484249425042514252425342544255425642574258425942604261426242634264426542664267426842694270427142724273427442754276427742784279428042814282428342844285428642874288428942904291429242934294429542964297429842994300430143024303430443054306430743084309431043114312431343144315431643174318431943204321432243234324432543264327432843294330433143324333433443354336433743384339434043414342434343444345434643474348434943504351435243534354435543564357435843594360436143624363436443654366436743684369437043714372437343744375437643774378437943804381438243834384438543864387438843894390439143924393439443954396439743984399440044014402440344044405440644074408440944104411441244134414441544164417441844194420442144224423442444254426442744284429443044314432443344344435443644374438443944404441444244434444444544464447444844494450445144524453445444554456445744584459446044614462446344644465446644674468446944704471447244734474447544764477447844794480448144824483448444854486448744884489449044914492449344944495449644974498449945004501450245034504450545064507450845094510451145124513451445154516451745184519452045214522452345244525452645274528452945304531453245334534453545364537453845394540454145424543454445454546454745484549455045514552455345544555455645574558455945604561456245634564456545664567456845694570457145724573457445754576457745784579458045814582458345844585458645874588458945904591459245934594459545964597459845994600460146024603460446054606460746084609461046114612461346144615461646174618461946204621462246234624462546264627462846294630463146324633463446354636463746384639464046414642464346444645464646474648464946504651465246534654465546564657465846594660466146624663466446654666466746684669467046714672467346744675467646774678467946804681468246834684468546864687468846894690469146924693469446954696469746984699470047014702470347044705470647074708470947104711471247134714471547164717471847194720472147224723472447254726472747284729473047314732473347344735473647374738473947404741474247434744474547464747474847494750475147524753475447554756475747584759476047614762476347644765476647674768476947704771477247734774477547764777477847794780478147824783478447854786478747884789479047914792479347944795479647974798479948004801480248034804480548064807480848094810481148124813481448154816481748184819482048214822482348244825482648274828482948304831483248334834483548364837483848394840484148424843484448454846484748484849485048514852485348544855485648574858485948604861486248634864486548664867486848694870487148724873487448754876487748784879488048814882488348844885488648874888488948904891489248934894489548964897489848994900490149024903490449054906490749084909491049114912491349144915491649174918491949204921492249234924492549264927492849294930493149324933493449354936493749384939494049414942494349444945494649474948494949504951495249534954495549564957495849594960496149624963496449654966496749684969497049714972497349744975497649774978497949804981498249834984498549864987498849894990499149924993499449954996499749984999500050015002500350045005500650075008500950105011501250135014501550165017501850195020502150225023502450255026502750285029503050315032503350345035503650375038503950405041504250435044504550465047504850495050505150525053505450555056505750585059506050615062506350645065506650675068506950705071507250735074507550765077507850795080508150825083508450855086508750885089509050915092509350945095509650975098509951005101510251035104510551065107510851095110511151125113511451155116511751185119512051215122512351245125512651275128512951305131513251335134513551365137513851395140514151425143514451455146514751485149515051515152515351545155515651575158515951605161516251635164516551665167516851695170517151725173517451755176517751785179518051815182518351845185518651875188518951905191519251935194519551965197519851995200520152025203520452055206520752085209521052115212521352145215521652175218521952205221522252235224522552265227522852295230523152325233523452355236523752385239524052415242524352445245524652475248524952505251525252535254525552565257525852595260526152625263526452655266526752685269527052715272527352745275527652775278527952805281528252835284528552865287528852895290529152925293529452955296529752985299530053015302530353045305530653075308530953105311531253135314531553165317531853195320532153225323532453255326532753285329533053315332533353345335533653375338533953405341534253435344534553465347534853495350535153525353535453555356535753585359536053615362536353645365536653675368536953705371537253735374537553765377537853795380538153825383538453855386538753885389539053915392539353945395539653975398539954005401540254035404540554065407540854095410541154125413541454155416541754185419542054215422542354245425542654275428542954305431543254335434543554365437543854395440544154425443544454455446544754485449545054515452545354545455545654575458545954605461546254635464546554665467546854695470547154725473547454755476547754785479548054815482548354845485548654875488548954905491549254935494549554965497549854995500550155025503550455055506550755085509551055115512551355145515551655175518551955205521552255235524552555265527552855295530553155325533553455355536553755385539554055415542554355445545554655475548554955505551555255535554555555565557555855595560556155625563556455655566556755685569557055715572557355745575557655775578557955805581558255835584558555865587558855895590559155925593559455955596559755985599560056015602560356045605560656075608560956105611561256135614561556165617561856195620562156225623562456255626562756285629563056315632563356345635563656375638563956405641564256435644564556465647564856495650565156525653565456555656565756585659566056615662566356645665566656675668566956705671567256735674567556765677567856795680568156825683568456855686568756885689569056915692569356945695569656975698569957005701570257035704570557065707570857095710571157125713571457155716571757185719572057215722572357245725572657275728572957305731573257335734573557365737573857395740574157425743574457455746574757485749575057515752575357545755575657575758575957605761576257635764576557665767576857695770577157725773577457755776577757785779578057815782578357845785578657875788578957905791579257935794579557965797579857995800580158025803580458055806580758085809581058115812581358145815581658175818581958205821582258235824582558265827582858295830583158325833583458355836583758385839584058415842584358445845584658475848584958505851585258535854585558565857585858595860586158625863586458655866586758685869587058715872587358745875587658775878587958805881588258835884588558865887588858895890589158925893589458955896589758985899590059015902590359045905590659075908590959105911591259135914591559165917591859195920592159225923592459255926592759285929593059315932593359345935593659375938593959405941594259435944594559465947594859495950595159525953595459555956595759585959596059615962596359645965596659675968596959705971597259735974597559765977597859795980598159825983598459855986598759885989599059915992599359945995599659975998599960006001600260036004600560066007600860096010601160126013601460156016601760186019602060216022602360246025602660276028602960306031603260336034603560366037603860396040604160426043604460456046604760486049605060516052605360546055605660576058605960606061606260636064606560666067606860696070607160726073607460756076607760786079608060816082608360846085608660876088608960906091609260936094609560966097609860996100610161026103610461056106610761086109611061116112611361146115611661176118611961206121612261236124612561266127612861296130613161326133613461356136613761386139614061416142614361446145614661476148614961506151615261536154615561566157615861596160616161626163616461656166616761686169617061716172617361746175617661776178617961806181618261836184618561866187618861896190619161926193619461956196619761986199620062016202620362046205620662076208620962106211621262136214621562166217621862196220622162226223622462256226622762286229623062316232623362346235623662376238623962406241624262436244624562466247624862496250625162526253625462556256625762586259626062616262626362646265626662676268626962706271627262736274627562766277627862796280628162826283628462856286628762886289629062916292629362946295629662976298629963006301630263036304630563066307630863096310631163126313631463156316631763186319632063216322632363246325632663276328632963306331633263336334633563366337633863396340634163426343634463456346634763486349635063516352635363546355635663576358635963606361636263636364636563666367636863696370637163726373637463756376637763786379638063816382638363846385638663876388638963906391639263936394639563966397639863996400640164026403640464056406640764086409641064116412641364146415641664176418641964206421642264236424642564266427642864296430643164326433643464356436643764386439644064416442644364446445644664476448644964506451645264536454645564566457645864596460646164626463646464656466646764686469647064716472647364746475647664776478647964806481648264836484648564866487648864896490649164926493649464956496649764986499650065016502650365046505650665076508650965106511651265136514651565166517651865196520652165226523652465256526652765286529653065316532653365346535653665376538653965406541654265436544654565466547654865496550655165526553655465556556655765586559656065616562656365646565656665676568656965706571657265736574657565766577657865796580658165826583658465856586658765886589659065916592659365946595659665976598659966006601660266036604660566066607660866096610661166126613661466156616661766186619662066216622662366246625662666276628662966306631663266336634663566366637663866396640664166426643664466456646664766486649665066516652665366546655665666576658665966606661666266636664666566666667666866696670667166726673667466756676667766786679668066816682668366846685668666876688668966906691669266936694669566966697669866996700670167026703670467056706670767086709671067116712671367146715671667176718671967206721672267236724672567266727672867296730673167326733673467356736673767386739674067416742674367446745674667476748674967506751675267536754675567566757675867596760676167626763676467656766676767686769677067716772677367746775677667776778677967806781678267836784678567866787678867896790679167926793679467956796679767986799680068016802680368046805680668076808680968106811681268136814681568166817681868196820682168226823682468256826682768286829683068316832683368346835683668376838683968406841684268436844684568466847684868496850685168526853685468556856685768586859686068616862686368646865686668676868686968706871687268736874687568766877687868796880688168826883688468856886688768886889689068916892689368946895689668976898689969006901690269036904690569066907690869096910691169126913691469156916691769186919692069216922692369246925692669276928692969306931693269336934693569366937693869396940694169426943694469456946694769486949695069516952695369546955695669576958695969606961696269636964696569666967696869696970697169726973697469756976697769786979698069816982698369846985698669876988698969906991699269936994699569966997699869997000700170027003700470057006700770087009701070117012701370147015701670177018701970207021702270237024702570267027702870297030703170327033703470357036703770387039704070417042704370447045704670477048704970507051705270537054705570567057705870597060706170627063706470657066706770687069707070717072707370747075707670777078707970807081708270837084708570867087708870897090709170927093709470957096709770987099710071017102710371047105710671077108710971107111711271137114711571167117711871197120712171227123712471257126712771287129713071317132713371347135713671377138713971407141714271437144714571467147714871497150715171527153715471557156715771587159716071617162716371647165716671677168716971707171717271737174717571767177717871797180718171827183718471857186718771887189719071917192719371947195719671977198719972007201720272037204720572067207720872097210721172127213721472157216721772187219722072217222722372247225722672277228722972307231723272337234723572367237723872397240724172427243724472457246724772487249725072517252725372547255725672577258725972607261726272637264726572667267726872697270727172727273727472757276727772787279728072817282728372847285728672877288728972907291729272937294729572967297729872997300730173027303730473057306730773087309731073117312731373147315731673177318731973207321732273237324732573267327732873297330733173327333733473357336733773387339734073417342734373447345734673477348734973507351735273537354735573567357735873597360736173627363736473657366736773687369737073717372737373747375737673777378737973807381738273837384738573867387738873897390739173927393739473957396739773987399740074017402740374047405740674077408740974107411741274137414741574167417741874197420742174227423742474257426742774287429743074317432743374347435743674377438743974407441744274437444744574467447744874497450745174527453745474557456745774587459746074617462746374647465746674677468746974707471747274737474747574767477747874797480748174827483748474857486748774887489749074917492749374947495749674977498749975007501750275037504750575067507750875097510751175127513751475157516751775187519752075217522752375247525752675277528752975307531753275337534753575367537753875397540754175427543754475457546754775487549755075517552755375547555755675577558755975607561756275637564756575667567756875697570757175727573757475757576757775787579758075817582758375847585758675877588758975907591759275937594759575967597759875997600760176027603760476057606760776087609761076117612761376147615761676177618761976207621762276237624762576267627762876297630763176327633763476357636763776387639764076417642764376447645764676477648764976507651765276537654765576567657765876597660766176627663766476657666766776687669767076717672767376747675767676777678767976807681768276837684768576867687768876897690769176927693769476957696769776987699770077017702770377047705770677077708770977107711771277137714771577167717771877197720772177227723772477257726772777287729773077317732773377347735773677377738773977407741774277437744774577467747774877497750775177527753775477557756775777587759776077617762776377647765776677677768776977707771777277737774777577767777777877797780778177827783778477857786778777887789779077917792779377947795779677977798779978007801780278037804780578067807780878097810781178127813781478157816781778187819782078217822782378247825782678277828782978307831783278337834783578367837783878397840784178427843784478457846784778487849785078517852785378547855785678577858785978607861786278637864786578667867786878697870787178727873787478757876787778787879788078817882788378847885788678877888788978907891789278937894789578967897789878997900790179027903790479057906790779087909791079117912791379147915791679177918791979207921792279237924792579267927792879297930793179327933793479357936793779387939794079417942794379447945794679477948794979507951795279537954795579567957795879597960796179627963796479657966796779687969797079717972797379747975797679777978797979807981798279837984798579867987798879897990799179927993799479957996799779987999800080018002800380048005800680078008800980108011801280138014801580168017801880198020802180228023802480258026802780288029803080318032803380348035803680378038803980408041804280438044804580468047804880498050805180528053805480558056805780588059806080618062806380648065806680678068806980708071807280738074807580768077807880798080808180828083808480858086808780888089809080918092809380948095809680978098809981008101810281038104810581068107810881098110811181128113811481158116811781188119812081218122812381248125812681278128812981308131813281338134813581368137813881398140814181428143814481458146814781488149815081518152815381548155815681578158815981608161816281638164816581668167816881698170817181728173817481758176817781788179818081818182818381848185818681878188818981908191819281938194819581968197819881998200820182028203820482058206820782088209821082118212821382148215821682178218821982208221822282238224822582268227822882298230823182328233823482358236823782388239824082418242824382448245824682478248824982508251825282538254825582568257825882598260826182628263826482658266826782688269827082718272827382748275827682778278827982808281828282838284828582868287828882898290829182928293829482958296829782988299830083018302830383048305830683078308830983108311831283138314831583168317831883198320832183228323832483258326832783288329833083318332833383348335833683378338833983408341834283438344834583468347834883498350835183528353835483558356835783588359836083618362836383648365836683678368836983708371837283738374837583768377837883798380838183828383838483858386838783888389839083918392839383948395839683978398839984008401840284038404840584068407840884098410841184128413841484158416841784188419842084218422842384248425842684278428842984308431843284338434843584368437843884398440844184428443844484458446844784488449845084518452845384548455845684578458845984608461846284638464846584668467846884698470847184728473847484758476847784788479848084818482848384848485848684878488848984908491849284938494849584968497849884998500850185028503850485058506850785088509851085118512851385148515851685178518851985208521852285238524852585268527852885298530853185328533853485358536853785388539854085418542854385448545854685478548854985508551855285538554855585568557855885598560856185628563856485658566856785688569857085718572857385748575857685778578857985808581858285838584858585868587858885898590859185928593859485958596859785988599860086018602860386048605860686078608860986108611861286138614861586168617861886198620862186228623862486258626862786288629863086318632863386348635863686378638863986408641864286438644864586468647864886498650865186528653865486558656865786588659866086618662866386648665866686678668866986708671867286738674867586768677867886798680868186828683868486858686868786888689869086918692869386948695869686978698869987008701870287038704870587068707870887098710871187128713871487158716871787188719872087218722872387248725872687278728872987308731873287338734873587368737873887398740874187428743874487458746874787488749875087518752875387548755875687578758875987608761876287638764876587668767876887698770877187728773877487758776877787788779878087818782878387848785878687878788878987908791879287938794879587968797879887998800880188028803880488058806880788088809881088118812881388148815881688178818881988208821882288238824882588268827882888298830883188328833883488358836883788388839884088418842884388448845884688478848884988508851885288538854885588568857885888598860886188628863886488658866886788688869887088718872887388748875887688778878887988808881888288838884888588868887888888898890889188928893889488958896889788988899890089018902890389048905890689078908890989108911891289138914891589168917891889198920892189228923892489258926892789288929893089318932893389348935893689378938893989408941894289438944894589468947894889498950895189528953895489558956895789588959896089618962896389648965896689678968896989708971897289738974897589768977897889798980898189828983898489858986898789888989899089918992899389948995899689978998899990009001900290039004900590069007900890099010901190129013901490159016901790189019902090219022902390249025902690279028902990309031903290339034903590369037903890399040904190429043904490459046904790489049905090519052905390549055905690579058905990609061906290639064906590669067906890699070907190729073907490759076907790789079908090819082908390849085908690879088908990909091909290939094909590969097909890999100910191029103910491059106910791089109911091119112911391149115911691179118911991209121912291239124912591269127912891299130913191329133913491359136913791389139914091419142914391449145914691479148914991509151915291539154915591569157915891599160916191629163916491659166916791689169917091719172917391749175917691779178917991809181918291839184918591869187918891899190919191929193919491959196919791989199920092019202920392049205920692079208920992109211921292139214921592169217921892199220922192229223922492259226922792289229923092319232923392349235923692379238923992409241924292439244924592469247924892499250925192529253925492559256925792589259926092619262926392649265926692679268926992709271927292739274927592769277927892799280928192829283928492859286928792889289929092919292929392949295929692979298929993009301930293039304930593069307930893099310931193129313931493159316931793189319932093219322932393249325932693279328932993309331933293339334933593369337933893399340934193429343934493459346934793489349935093519352935393549355935693579358935993609361936293639364936593669367936893699370937193729373937493759376937793789379938093819382938393849385938693879388938993909391939293939394939593969397939893999400940194029403940494059406940794089409941094119412941394149415941694179418941994209421942294239424942594269427942894299430943194329433943494359436943794389439944094419442944394449445944694479448944994509451945294539454945594569457945894599460946194629463946494659466946794689469947094719472947394749475947694779478947994809481948294839484948594869487948894899490949194929493949494959496949794989499950095019502950395049505950695079508950995109511951295139514951595169517951895199520952195229523952495259526952795289529953095319532953395349535953695379538953995409541954295439544954595469547954895499550955195529553955495559556955795589559956095619562956395649565956695679568956995709571957295739574957595769577957895799580958195829583958495859586958795889589959095919592959395949595959695979598959996009601960296039604960596069607960896099610961196129613961496159616961796189619962096219622962396249625962696279628962996309631963296339634963596369637963896399640964196429643964496459646964796489649965096519652965396549655965696579658965996609661966296639664966596669667966896699670967196729673967496759676967796789679968096819682968396849685968696879688968996909691969296939694969596969697969896999700970197029703970497059706970797089709971097119712971397149715971697179718971997209721972297239724972597269727972897299730973197329733973497359736973797389739974097419742974397449745974697479748974997509751975297539754975597569757975897599760976197629763976497659766976797689769977097719772977397749775977697779778977997809781978297839784978597869787978897899790979197929793979497959796979797989799980098019802980398049805980698079808980998109811981298139814981598169817981898199820982198229823982498259826982798289829983098319832983398349835983698379838983998409841984298439844984598469847984898499850985198529853985498559856985798589859986098619862986398649865986698679868986998709871987298739874987598769877987898799880988198829883988498859886988798889889989098919892989398949895989698979898989999009901990299039904990599069907990899099910991199129913991499159916991799189919992099219922992399249925992699279928992999309931993299339934993599369937993899399940994199429943994499459946994799489949995099519952995399549955995699579958995999609961996299639964996599669967996899699970997199729973997499759976997799789979998099819982998399849985998699879988998999909991999299939994999599969997999899991000010001100021000310004100051000610007100081000910010100111001210013100141001510016100171001810019100201002110022100231002410025100261002710028100291003010031100321003310034100351003610037100381003910040100411004210043100441004510046100471004810049100501005110052100531005410055100561005710058100591006010061100621006310064100651006610067100681006910070100711007210073100741007510076100771007810079100801008110082100831008410085100861008710088100891009010091100921009310094100951009610097100981009910100101011010210103101041010510106101071010810109101101011110112101131011410115101161011710118101191012010121101221012310124101251012610127101281012910130101311013210133101341013510136101371013810139101401014110142101431014410145101461014710148101491015010151101521015310154101551015610157101581015910160101611016210163101641016510166101671016810169101701017110172101731017410175101761017710178101791018010181101821018310184101851018610187101881018910190101911019210193101941019510196101971019810199102001020110202102031020410205102061020710208102091021010211102121021310214102151021610217102181021910220102211022210223102241022510226102271022810229102301023110232102331023410235102361023710238102391024010241102421024310244102451024610247102481024910250102511025210253102541025510256102571025810259102601026110262102631026410265102661026710268102691027010271102721027310274102751027610277102781027910280102811028210283102841028510286102871028810289102901029110292102931029410295102961029710298102991030010301103021030310304103051030610307103081030910310103111031210313103141031510316103171031810319103201032110322103231032410325103261032710328103291033010331103321033310334103351033610337103381033910340103411034210343103441034510346103471034810349103501035110352103531035410355103561035710358103591036010361103621036310364103651036610367103681036910370103711037210373103741037510376103771037810379103801038110382103831038410385103861038710388103891039010391103921039310394103951039610397103981039910400104011040210403104041040510406104071040810409104101041110412104131041410415104161041710418104191042010421104221042310424104251042610427104281042910430104311043210433104341043510436104371043810439104401044110442104431044410445104461044710448104491045010451104521045310454104551045610457104581045910460104611046210463104641046510466104671046810469104701047110472104731047410475104761047710478104791048010481104821048310484104851048610487104881048910490104911049210493104941049510496104971049810499105001050110502105031050410505105061050710508105091051010511105121051310514105151051610517105181051910520105211052210523105241052510526105271052810529105301053110532105331053410535105361053710538105391054010541105421054310544105451054610547105481054910550105511055210553105541055510556105571055810559105601056110562105631056410565105661056710568105691057010571105721057310574105751057610577105781057910580105811058210583105841058510586105871058810589105901059110592105931059410595105961059710598105991060010601106021060310604106051060610607106081060910610106111061210613106141061510616106171061810619106201062110622106231062410625106261062710628106291063010631106321063310634106351063610637106381063910640106411064210643106441064510646106471064810649106501065110652106531065410655106561065710658106591066010661106621066310664106651066610667106681066910670106711067210673106741067510676106771067810679106801068110682106831068410685106861068710688106891069010691106921069310694106951069610697106981069910700107011070210703107041070510706107071070810709107101071110712107131071410715107161071710718107191072010721107221072310724107251072610727107281072910730107311073210733107341073510736107371073810739107401074110742107431074410745107461074710748107491075010751107521075310754107551075610757107581075910760107611076210763107641076510766107671076810769107701077110772107731077410775107761077710778107791078010781107821078310784107851078610787107881078910790107911079210793107941079510796107971079810799108001080110802108031080410805108061080710808108091081010811108121081310814108151081610817108181081910820108211082210823108241082510826108271082810829108301083110832108331083410835108361083710838108391084010841108421084310844108451084610847108481084910850108511085210853108541085510856108571085810859108601086110862108631086410865108661086710868108691087010871108721087310874108751087610877108781087910880108811088210883108841088510886108871088810889108901089110892108931089410895108961089710898108991090010901109021090310904109051090610907109081090910910109111091210913109141091510916109171091810919109201092110922109231092410925109261092710928109291093010931109321093310934109351093610937109381093910940109411094210943109441094510946109471094810949109501095110952109531095410955109561095710958109591096010961109621096310964109651096610967109681096910970109711097210973109741097510976109771097810979109801098110982109831098410985109861098710988109891099010991109921099310994109951099610997109981099911000110011100211003110041100511006110071100811009110101101111012110131101411015110161101711018110191102011021110221102311024110251102611027110281102911030110311103211033110341103511036110371103811039110401104111042110431104411045110461104711048110491105011051110521105311054110551105611057110581105911060110611106211063110641106511066110671106811069110701107111072110731107411075110761107711078110791108011081110821108311084110851108611087110881108911090110911109211093110941109511096110971109811099111001110111102111031110411105111061110711108111091111011111111121111311114111151111611117111181111911120111211112211123111241112511126111271112811129111301113111132111331113411135111361113711138111391114011141111421114311144111451114611147111481114911150111511115211153111541115511156111571115811159111601116111162111631116411165111661116711168111691117011171111721117311174111751117611177111781117911180111811118211183111841118511186111871118811189111901119111192111931119411195111961119711198111991120011201112021120311204112051120611207112081120911210112111121211213112141121511216112171121811219112201122111222112231122411225112261122711228112291123011231112321123311234112351123611237112381123911240112411124211243112441124511246112471124811249112501125111252112531125411255112561125711258112591126011261112621126311264112651126611267112681126911270112711127211273112741127511276112771127811279112801128111282112831128411285112861128711288112891129011291112921129311294112951129611297112981129911300113011130211303113041130511306113071130811309113101131111312113131131411315113161131711318113191132011321113221132311324113251132611327113281132911330113311133211333113341133511336113371133811339113401134111342113431134411345113461134711348113491135011351113521135311354113551135611357113581135911360113611136211363113641136511366113671136811369113701137111372113731137411375113761137711378113791138011381113821138311384113851138611387113881138911390113911139211393113941139511396113971139811399114001140111402114031140411405114061140711408114091141011411114121141311414114151141611417114181141911420114211142211423114241142511426114271142811429114301143111432114331143411435114361143711438114391144011441114421144311444114451144611447114481144911450114511145211453114541145511456114571145811459114601146111462114631146411465114661146711468114691147011471114721147311474114751147611477114781147911480114811148211483114841148511486114871148811489114901149111492114931149411495114961149711498114991150011501115021150311504115051150611507115081150911510115111151211513115141151511516115171151811519115201152111522115231152411525115261152711528115291153011531115321153311534115351153611537115381153911540115411154211543115441154511546115471154811549115501155111552115531155411555115561155711558115591156011561115621156311564115651156611567115681156911570115711157211573115741157511576115771157811579115801158111582115831158411585115861158711588115891159011591115921159311594115951159611597115981159911600116011160211603116041160511606116071160811609116101161111612116131161411615116161161711618116191162011621116221162311624116251162611627116281162911630116311163211633116341163511636116371163811639116401164111642116431164411645116461164711648116491165011651116521165311654116551165611657116581165911660116611166211663116641166511666116671166811669116701167111672116731167411675116761167711678116791168011681116821168311684116851168611687116881168911690116911169211693116941169511696116971169811699117001170111702117031170411705117061170711708117091171011711117121171311714117151171611717117181171911720117211172211723117241172511726117271172811729117301173111732117331173411735117361173711738117391174011741117421174311744117451174611747117481174911750117511175211753117541175511756117571175811759117601176111762117631176411765117661176711768117691177011771117721177311774117751177611777117781177911780117811178211783117841178511786117871178811789117901179111792117931179411795117961179711798117991180011801118021180311804118051180611807118081180911810118111181211813118141181511816118171181811819118201182111822118231182411825118261182711828118291183011831118321183311834118351183611837118381183911840118411184211843118441184511846118471184811849118501185111852118531185411855118561185711858118591186011861118621186311864118651186611867118681186911870118711187211873118741187511876118771187811879118801188111882118831188411885118861188711888118891189011891118921189311894118951189611897118981189911900119011190211903119041190511906119071190811909119101191111912119131191411915119161191711918119191192011921119221192311924119251192611927119281192911930119311193211933119341193511936119371193811939119401194111942119431194411945119461194711948119491195011951119521195311954119551195611957119581195911960119611196211963119641196511966119671196811969119701197111972119731197411975119761197711978119791198011981119821198311984119851198611987119881198911990119911199211993119941199511996119971199811999120001200112002120031200412005120061200712008120091201012011120121201312014120151201612017120181201912020120211202212023120241202512026120271202812029120301203112032120331203412035120361203712038120391204012041120421204312044120451204612047120481204912050120511205212053120541205512056120571205812059120601206112062120631206412065120661206712068120691207012071120721207312074120751207612077120781207912080120811208212083120841208512086120871208812089120901209112092120931209412095120961209712098120991210012101121021210312104121051210612107121081210912110121111211212113121141211512116121171211812119121201212112122121231212412125121261212712128121291213012131121321213312134121351213612137121381213912140121411214212143121441214512146121471214812149121501215112152121531215412155121561215712158121591216012161121621216312164121651216612167121681216912170121711217212173121741217512176121771217812179121801218112182121831218412185121861218712188121891219012191121921219312194121951219612197121981219912200122011220212203122041220512206122071220812209122101221112212122131221412215122161221712218122191222012221122221222312224122251222612227122281222912230122311223212233122341223512236122371223812239122401224112242122431224412245122461224712248122491225012251122521225312254122551225612257122581225912260122611226212263122641226512266122671226812269122701227112272122731227412275122761227712278122791228012281122821228312284122851228612287122881228912290122911229212293122941229512296122971229812299123001230112302123031230412305123061230712308123091231012311123121231312314123151231612317123181231912320123211232212323123241232512326123271232812329123301233112332123331233412335123361233712338123391234012341123421234312344123451234612347123481234912350123511235212353123541235512356123571235812359123601236112362123631236412365123661236712368123691237012371123721237312374123751237612377123781237912380123811238212383123841238512386123871238812389123901239112392123931239412395123961239712398123991240012401124021240312404124051240612407124081240912410124111241212413124141241512416124171241812419124201242112422124231242412425124261242712428124291243012431124321243312434124351243612437124381243912440124411244212443124441244512446124471244812449124501245112452124531245412455124561245712458124591246012461124621246312464124651246612467124681246912470124711247212473124741247512476124771247812479124801248112482124831248412485124861248712488124891249012491124921249312494124951249612497124981249912500125011250212503125041250512506125071250812509125101251112512125131251412515125161251712518125191252012521125221252312524125251252612527125281252912530125311253212533125341253512536125371253812539125401254112542125431254412545125461254712548125491255012551125521255312554125551255612557125581255912560125611256212563125641256512566125671256812569125701257112572125731257412575125761257712578125791258012581125821258312584125851258612587125881258912590125911259212593125941259512596125971259812599126001260112602126031260412605126061260712608126091261012611126121261312614126151261612617126181261912620126211262212623126241262512626126271262812629126301263112632126331263412635126361263712638126391264012641126421264312644126451264612647126481264912650126511265212653126541265512656126571265812659126601266112662126631266412665126661266712668126691267012671126721267312674126751267612677126781267912680126811268212683126841268512686126871268812689126901269112692126931269412695126961269712698126991270012701127021270312704127051270612707127081270912710127111271212713127141271512716127171271812719127201272112722127231272412725127261272712728127291273012731127321273312734127351273612737127381273912740127411274212743127441274512746127471274812749127501275112752127531275412755127561275712758127591276012761127621276312764127651276612767127681276912770127711277212773127741277512776127771277812779127801278112782127831278412785127861278712788127891279012791127921279312794127951279612797127981279912800128011280212803128041280512806128071280812809128101281112812128131281412815128161281712818128191282012821128221282312824128251282612827128281282912830128311283212833128341283512836128371283812839128401284112842128431284412845128461284712848128491285012851128521285312854128551285612857128581285912860128611286212863128641286512866128671286812869128701287112872128731287412875128761287712878128791288012881128821288312884128851288612887128881288912890128911289212893128941289512896128971289812899129001290112902129031290412905129061290712908129091291012911129121291312914129151291612917129181291912920129211292212923129241292512926129271292812929129301293112932129331293412935129361293712938129391294012941129421294312944129451294612947129481294912950129511295212953129541295512956129571295812959129601296112962129631296412965129661296712968129691297012971129721297312974129751297612977129781297912980129811298212983129841298512986129871298812989129901299112992129931299412995129961299712998129991300013001130021300313004130051300613007130081300913010130111301213013130141301513016130171301813019130201302113022130231302413025130261302713028130291303013031130321303313034130351303613037130381303913040130411304213043130441304513046130471304813049130501305113052130531305413055130561305713058130591306013061130621306313064130651306613067130681306913070130711307213073130741307513076130771307813079130801308113082130831308413085130861308713088130891309013091130921309313094130951309613097130981309913100131011310213103131041310513106131071310813109131101311113112131131311413115131161311713118131191312013121131221312313124131251312613127131281312913130131311313213133131341313513136131371313813139131401314113142131431314413145131461314713148131491315013151131521315313154131551315613157131581315913160131611316213163131641316513166131671316813169131701317113172131731317413175131761317713178131791318013181131821318313184131851318613187131881318913190131911319213193131941319513196131971319813199132001320113202132031320413205132061320713208132091321013211132121321313214132151321613217132181321913220132211322213223132241322513226132271322813229132301323113232132331323413235132361323713238132391324013241132421324313244132451324613247132481324913250132511325213253132541325513256132571325813259132601326113262132631326413265132661326713268132691327013271132721327313274132751327613277132781327913280132811328213283132841328513286132871328813289132901329113292132931329413295132961329713298132991330013301133021330313304133051330613307133081330913310133111331213313133141331513316133171331813319133201332113322133231332413325133261332713328133291333013331133321333313334133351333613337133381333913340133411334213343133441334513346133471334813349133501335113352133531335413355133561335713358133591336013361133621336313364133651336613367133681336913370133711337213373133741337513376133771337813379133801338113382133831338413385133861338713388133891339013391133921339313394133951339613397133981339913400134011340213403134041340513406134071340813409134101341113412134131341413415134161341713418134191342013421134221342313424134251342613427134281342913430134311343213433134341343513436134371343813439134401344113442134431344413445134461344713448134491345013451134521345313454134551345613457134581345913460134611346213463134641346513466134671346813469134701347113472134731347413475134761347713478134791348013481134821348313484134851348613487134881348913490134911349213493134941349513496134971349813499135001350113502135031350413505135061350713508135091351013511135121351313514135151351613517135181351913520135211352213523135241352513526135271352813529135301353113532135331353413535135361353713538135391354013541135421354313544135451354613547135481354913550135511355213553135541355513556135571355813559135601356113562135631356413565135661356713568135691357013571135721357313574135751357613577135781357913580135811358213583135841358513586135871358813589135901359113592135931359413595135961359713598135991360013601136021360313604136051360613607136081360913610136111361213613136141361513616136171361813619136201362113622136231362413625136261362713628136291363013631136321363313634136351363613637136381363913640136411364213643136441364513646136471364813649136501365113652136531365413655136561365713658136591366013661136621366313664136651366613667136681366913670136711367213673136741367513676136771367813679136801368113682136831368413685136861368713688136891369013691136921369313694136951369613697136981369913700137011370213703137041370513706137071370813709137101371113712137131371413715137161371713718137191372013721137221372313724137251372613727137281372913730137311373213733137341373513736137371373813739137401374113742137431374413745137461374713748137491375013751137521375313754137551375613757137581375913760137611376213763137641376513766137671376813769137701377113772137731377413775137761377713778137791378013781137821378313784137851378613787137881378913790137911379213793137941379513796137971379813799138001380113802138031380413805138061380713808138091381013811138121381313814138151381613817138181381913820138211382213823138241382513826138271382813829138301383113832138331383413835138361383713838138391384013841138421384313844138451384613847138481384913850138511385213853138541385513856138571385813859138601386113862138631386413865138661386713868138691387013871138721387313874138751387613877138781387913880138811388213883138841388513886138871388813889138901389113892138931389413895138961389713898138991390013901139021390313904139051390613907139081390913910139111391213913139141391513916139171391813919139201392113922139231392413925139261392713928139291393013931139321393313934139351393613937139381393913940139411394213943139441394513946139471394813949139501395113952139531395413955139561395713958139591396013961139621396313964139651396613967139681396913970139711397213973139741397513976139771397813979139801398113982139831398413985139861398713988139891399013991139921399313994139951399613997139981399914000140011400214003140041400514006140071400814009140101401114012140131401414015140161401714018140191402014021140221402314024140251402614027140281402914030140311403214033140341403514036140371403814039140401404114042140431404414045140461404714048140491405014051140521405314054140551405614057140581405914060140611406214063140641406514066140671406814069140701407114072140731407414075140761407714078140791408014081140821408314084140851408614087140881408914090140911409214093140941409514096140971409814099141001410114102141031410414105141061410714108141091411014111141121411314114141151411614117141181411914120141211412214123141241412514126141271412814129141301413114132141331413414135141361413714138141391414014141141421414314144141451414614147141481414914150141511415214153141541415514156141571415814159141601416114162141631416414165141661416714168141691417014171141721417314174141751417614177141781417914180141811418214183141841418514186141871418814189141901419114192141931419414195141961419714198141991420014201142021420314204142051420614207142081420914210142111421214213142141421514216142171421814219142201422114222142231422414225142261422714228142291423014231142321423314234142351423614237142381423914240142411424214243142441424514246142471424814249142501425114252142531425414255142561425714258142591426014261142621426314264142651426614267142681426914270142711427214273142741427514276142771427814279142801428114282142831428414285142861428714288142891429014291142921429314294142951429614297142981429914300143011430214303143041430514306143071430814309143101431114312143131431414315143161431714318143191432014321143221432314324143251432614327143281432914330143311433214333143341433514336143371433814339143401434114342143431434414345143461434714348143491435014351143521435314354143551435614357143581435914360143611436214363143641436514366143671436814369143701437114372143731437414375143761437714378143791438014381143821438314384143851438614387143881438914390143911439214393143941439514396143971439814399144001440114402144031440414405144061440714408144091441014411144121441314414144151441614417144181441914420144211442214423144241442514426144271442814429144301443114432144331443414435144361443714438144391444014441144421444314444144451444614447144481444914450144511445214453144541445514456144571445814459144601446114462144631446414465144661446714468144691447014471144721447314474144751447614477144781447914480144811448214483144841448514486144871448814489144901449114492144931449414495144961449714498144991450014501145021450314504145051450614507145081450914510145111451214513145141451514516145171451814519145201452114522145231452414525145261452714528145291453014531145321453314534145351453614537145381453914540145411454214543145441454514546145471454814549145501455114552145531455414555145561455714558145591456014561145621456314564145651456614567145681456914570145711457214573145741457514576145771457814579145801458114582145831458414585145861458714588145891459014591145921459314594145951459614597145981459914600146011460214603146041460514606146071460814609146101461114612146131461414615146161461714618146191462014621146221462314624146251462614627146281462914630146311463214633146341463514636146371463814639146401464114642146431464414645146461464714648146491465014651146521465314654146551465614657146581465914660146611466214663146641466514666146671466814669146701467114672146731467414675146761467714678146791468014681146821468314684146851468614687146881468914690146911469214693146941469514696146971469814699147001470114702147031470414705147061470714708147091471014711147121471314714147151471614717147181471914720147211472214723147241472514726147271472814729147301473114732147331473414735147361473714738147391474014741147421474314744147451474614747147481474914750147511475214753147541475514756147571475814759147601476114762147631476414765147661476714768147691477014771147721477314774147751477614777147781477914780147811478214783147841478514786147871478814789147901479114792147931479414795147961479714798147991480014801148021480314804148051480614807148081480914810148111481214813148141481514816148171481814819148201482114822148231482414825148261482714828148291483014831148321483314834148351483614837148381483914840148411484214843148441484514846148471484814849148501485114852148531485414855148561485714858148591486014861148621486314864148651486614867148681486914870148711487214873148741487514876148771487814879148801488114882148831488414885148861488714888148891489014891148921489314894148951489614897148981489914900149011490214903149041490514906149071490814909149101491114912149131491414915149161491714918149191492014921149221492314924149251492614927149281492914930149311493214933149341493514936149371493814939149401494114942149431494414945149461494714948149491495014951149521495314954149551495614957149581495914960149611496214963149641496514966149671496814969149701497114972149731497414975149761497714978149791498014981149821498314984149851498614987149881498914990149911499214993149941499514996149971499814999150001500115002150031500415005150061500715008150091501015011150121501315014150151501615017150181501915020150211502215023150241502515026150271502815029150301503115032150331503415035150361503715038150391504015041150421504315044150451504615047150481504915050150511505215053150541505515056150571505815059150601506115062150631506415065150661506715068150691507015071150721507315074150751507615077150781507915080150811508215083150841508515086150871508815089150901509115092150931509415095150961509715098150991510015101151021510315104151051510615107151081510915110151111511215113151141511515116151171511815119151201512115122151231512415125151261512715128151291513015131151321513315134151351513615137151381513915140151411514215143151441514515146151471514815149151501515115152151531515415155151561515715158151591516015161151621516315164151651516615167151681516915170151711517215173151741517515176151771517815179151801518115182151831518415185151861518715188151891519015191151921519315194151951519615197151981519915200152011520215203152041520515206152071520815209152101521115212152131521415215152161521715218152191522015221152221522315224152251522615227152281522915230152311523215233152341523515236152371523815239152401524115242152431524415245152461524715248152491525015251152521525315254152551525615257152581525915260152611526215263152641526515266152671526815269152701527115272152731527415275152761527715278152791528015281152821528315284152851528615287152881528915290152911529215293152941529515296152971529815299153001530115302153031530415305153061530715308153091531015311153121531315314153151531615317153181531915320153211532215323153241532515326153271532815329153301533115332153331533415335153361533715338153391534015341153421534315344153451534615347153481534915350153511535215353153541535515356153571535815359153601536115362153631536415365153661536715368153691537015371153721537315374153751537615377153781537915380153811538215383153841538515386153871538815389153901539115392153931539415395153961539715398153991540015401154021540315404154051540615407154081540915410154111541215413154141541515416154171541815419154201542115422154231542415425154261542715428154291543015431154321543315434154351543615437154381543915440154411544215443154441544515446154471544815449154501545115452154531545415455154561545715458154591546015461154621546315464154651546615467154681546915470154711547215473154741547515476154771547815479154801548115482154831548415485154861548715488154891549015491154921549315494154951549615497154981549915500155011550215503155041550515506155071550815509155101551115512155131551415515155161551715518155191552015521155221552315524155251552615527155281552915530155311553215533155341553515536155371553815539155401554115542155431554415545155461554715548155491555015551155521555315554155551555615557155581555915560155611556215563155641556515566155671556815569155701557115572155731557415575155761557715578155791558015581155821558315584155851558615587155881558915590155911559215593155941559515596155971559815599156001560115602156031560415605156061560715608156091561015611156121561315614156151561615617156181561915620156211562215623156241562515626156271562815629156301563115632156331563415635156361563715638156391564015641156421564315644156451564615647156481564915650156511565215653156541565515656156571565815659156601566115662156631566415665156661566715668156691567015671156721567315674156751567615677156781567915680156811568215683156841568515686156871568815689156901569115692156931569415695156961569715698156991570015701157021570315704157051570615707157081570915710157111571215713157141571515716157171571815719157201572115722157231572415725157261572715728157291573015731157321573315734157351573615737157381573915740157411574215743157441574515746157471574815749157501575115752157531575415755157561575715758157591576015761157621576315764157651576615767157681576915770157711577215773157741577515776157771577815779157801578115782157831578415785157861578715788157891579015791157921579315794157951579615797157981579915800158011580215803158041580515806158071580815809158101581115812158131581415815158161581715818158191582015821158221582315824158251582615827158281582915830158311583215833158341583515836158371583815839158401584115842158431584415845158461584715848158491585015851158521585315854158551585615857158581585915860158611586215863158641586515866158671586815869158701587115872158731587415875158761587715878158791588015881158821588315884158851588615887158881588915890158911589215893158941589515896158971589815899159001590115902159031590415905159061590715908159091591015911159121591315914159151591615917159181591915920159211592215923159241592515926159271592815929159301593115932159331593415935159361593715938159391594015941159421594315944159451594615947159481594915950159511595215953159541595515956159571595815959159601596115962159631596415965159661596715968159691597015971159721597315974159751597615977159781597915980159811598215983159841598515986159871598815989159901599115992159931599415995159961599715998159991600016001160021600316004160051600616007160081600916010160111601216013160141601516016160171601816019160201602116022160231602416025160261602716028160291603016031160321603316034160351603616037160381603916040160411604216043160441604516046160471604816049160501605116052160531605416055160561605716058160591606016061160621606316064160651606616067160681606916070160711607216073160741607516076160771607816079160801608116082160831608416085160861608716088160891609016091160921609316094160951609616097160981609916100161011610216103161041610516106161071610816109161101611116112161131611416115161161611716118161191612016121161221612316124161251612616127161281612916130161311613216133161341613516136161371613816139161401614116142161431614416145161461614716148161491615016151161521615316154161551615616157161581615916160161611616216163161641616516166161671616816169161701617116172161731617416175161761617716178161791618016181161821618316184161851618616187161881618916190161911619216193161941619516196161971619816199162001620116202162031620416205162061620716208162091621016211162121621316214162151621616217162181621916220162211622216223162241622516226162271622816229162301623116232162331623416235162361623716238162391624016241162421624316244162451624616247162481624916250162511625216253162541625516256162571625816259162601626116262162631626416265162661626716268162691627016271162721627316274162751627616277162781627916280162811628216283162841628516286162871628816289162901629116292162931629416295162961629716298162991630016301163021630316304163051630616307163081630916310163111631216313163141631516316163171631816319163201632116322163231632416325163261632716328163291633016331163321633316334163351633616337163381633916340163411634216343163441634516346163471634816349163501635116352163531635416355163561635716358163591636016361163621636316364163651636616367163681636916370163711637216373163741637516376163771637816379163801638116382163831638416385163861638716388163891639016391163921639316394163951639616397163981639916400164011640216403164041640516406164071640816409164101641116412164131641416415164161641716418164191642016421164221642316424164251642616427164281642916430164311643216433164341643516436164371643816439164401644116442164431644416445164461644716448164491645016451164521645316454164551645616457164581645916460164611646216463164641646516466164671646816469164701647116472164731647416475164761647716478164791648016481164821648316484164851648616487164881648916490164911649216493164941649516496164971649816499165001650116502165031650416505165061650716508165091651016511165121651316514165151651616517165181651916520165211652216523165241652516526165271652816529165301653116532165331653416535165361653716538165391654016541165421654316544165451654616547165481654916550165511655216553165541655516556165571655816559165601656116562165631656416565165661656716568165691657016571165721657316574165751657616577165781657916580165811658216583165841658516586165871658816589165901659116592165931659416595165961659716598165991660016601166021660316604166051660616607166081660916610166111661216613166141661516616166171661816619166201662116622166231662416625166261662716628166291663016631166321663316634166351663616637166381663916640166411664216643166441664516646166471664816649166501665116652166531665416655166561665716658166591666016661166621666316664166651666616667166681666916670166711667216673166741667516676166771667816679166801668116682166831668416685166861668716688166891669016691166921669316694166951669616697166981669916700167011670216703167041670516706167071670816709167101671116712167131671416715167161671716718167191672016721167221672316724167251672616727167281672916730167311673216733167341673516736167371673816739167401674116742167431674416745167461674716748167491675016751167521675316754167551675616757167581675916760167611676216763167641676516766167671676816769167701677116772167731677416775167761677716778167791678016781167821678316784167851678616787167881678916790167911679216793167941679516796167971679816799168001680116802168031680416805168061680716808168091681016811168121681316814168151681616817168181681916820168211682216823168241682516826168271682816829168301683116832168331683416835168361683716838168391684016841168421684316844168451684616847168481684916850168511685216853168541685516856168571685816859168601686116862168631686416865168661686716868168691687016871168721687316874168751687616877168781687916880168811688216883168841688516886168871688816889168901689116892168931689416895168961689716898168991690016901169021690316904169051690616907169081690916910169111691216913169141691516916169171691816919169201692116922169231692416925169261692716928169291693016931169321693316934169351693616937169381693916940169411694216943169441694516946169471694816949169501695116952169531695416955169561695716958169591696016961169621696316964169651696616967169681696916970169711697216973169741697516976169771697816979169801698116982169831698416985169861698716988169891699016991169921699316994169951699616997169981699917000170011700217003170041700517006170071700817009170101701117012170131701417015170161701717018170191702017021170221702317024170251702617027170281702917030170311703217033170341703517036170371703817039170401704117042170431704417045170461704717048170491705017051170521705317054170551705617057170581705917060170611706217063170641706517066170671706817069170701707117072170731707417075170761707717078170791708017081170821708317084170851708617087170881708917090170911709217093170941709517096170971709817099171001710117102171031710417105171061710717108171091711017111171121711317114171151711617117171181711917120171211712217123171241712517126171271712817129171301713117132171331713417135171361713717138171391714017141171421714317144171451714617147171481714917150171511715217153171541715517156171571715817159171601716117162171631716417165171661716717168171691717017171171721717317174171751717617177171781717917180171811718217183171841718517186171871718817189171901719117192171931719417195171961719717198171991720017201172021720317204172051720617207172081720917210172111721217213172141721517216172171721817219172201722117222172231722417225172261722717228172291723017231172321723317234172351723617237172381723917240172411724217243172441724517246172471724817249172501725117252172531725417255172561725717258172591726017261172621726317264172651726617267172681726917270172711727217273172741727517276172771727817279172801728117282172831728417285172861728717288172891729017291172921729317294172951729617297172981729917300173011730217303173041730517306173071730817309173101731117312173131731417315173161731717318173191732017321173221732317324173251732617327173281732917330173311733217333173341733517336173371733817339173401734117342173431734417345173461734717348173491735017351173521735317354173551735617357173581735917360173611736217363173641736517366173671736817369173701737117372173731737417375173761737717378173791738017381173821738317384173851738617387173881738917390173911739217393173941739517396173971739817399174001740117402174031740417405174061740717408174091741017411174121741317414174151741617417174181741917420174211742217423174241742517426174271742817429174301743117432174331743417435174361743717438174391744017441174421744317444174451744617447174481744917450174511745217453174541745517456174571745817459174601746117462174631746417465174661746717468174691747017471174721747317474174751747617477174781747917480174811748217483174841748517486174871748817489174901749117492174931749417495174961749717498174991750017501175021750317504175051750617507175081750917510175111751217513175141751517516175171751817519175201752117522175231752417525175261752717528175291753017531175321753317534175351753617537175381753917540175411754217543175441754517546175471754817549175501755117552175531755417555175561755717558175591756017561175621756317564175651756617567175681756917570175711757217573175741757517576175771757817579175801758117582175831758417585175861758717588175891759017591175921759317594175951759617597175981759917600176011760217603176041760517606176071760817609176101761117612176131761417615176161761717618176191762017621176221762317624176251762617627176281762917630176311763217633176341763517636176371763817639176401764117642176431764417645176461764717648176491765017651176521765317654176551765617657176581765917660176611766217663176641766517666176671766817669176701767117672176731767417675176761767717678176791768017681176821768317684176851768617687176881768917690176911769217693176941769517696176971769817699177001770117702177031770417705177061770717708177091771017711177121771317714177151771617717177181771917720177211772217723177241772517726177271772817729177301773117732177331773417735177361773717738177391774017741177421774317744177451774617747177481774917750177511775217753177541775517756177571775817759177601776117762177631776417765177661776717768177691777017771177721777317774177751777617777177781777917780177811778217783177841778517786177871778817789177901779117792177931779417795177961779717798177991780017801178021780317804178051780617807178081780917810178111781217813178141781517816178171781817819178201782117822178231782417825178261782717828178291783017831178321783317834178351783617837178381783917840178411784217843178441784517846178471784817849178501785117852178531785417855178561785717858178591786017861178621786317864178651786617867178681786917870178711787217873178741787517876178771787817879178801788117882178831788417885178861788717888178891789017891178921789317894178951789617897178981789917900179011790217903179041790517906179071790817909179101791117912179131791417915179161791717918179191792017921179221792317924179251792617927179281792917930179311793217933179341793517936179371793817939179401794117942179431794417945179461794717948179491795017951179521795317954179551795617957179581795917960179611796217963179641796517966179671796817969179701797117972179731797417975179761797717978179791798017981179821798317984179851798617987179881798917990179911799217993179941799517996179971799817999180001800118002180031800418005180061800718008180091801018011180121801318014180151801618017180181801918020180211802218023180241802518026180271802818029180301803118032180331803418035180361803718038180391804018041180421804318044180451804618047180481804918050180511805218053180541805518056180571805818059180601806118062180631806418065180661806718068180691807018071180721807318074180751807618077180781807918080180811808218083180841808518086180871808818089180901809118092180931809418095180961809718098180991810018101181021810318104181051810618107181081810918110181111811218113181141811518116181171811818119181201812118122181231812418125181261812718128181291813018131181321813318134181351813618137181381813918140181411814218143181441814518146181471814818149181501815118152181531815418155181561815718158181591816018161181621816318164181651816618167181681816918170181711817218173181741817518176181771817818179181801818118182181831818418185181861818718188181891819018191181921819318194181951819618197181981819918200182011820218203182041820518206182071820818209182101821118212182131821418215182161821718218182191822018221182221822318224182251822618227182281822918230182311823218233182341823518236182371823818239182401824118242182431824418245182461824718248182491825018251182521825318254182551825618257182581825918260182611826218263182641826518266182671826818269182701827118272182731827418275182761827718278182791828018281182821828318284182851828618287182881828918290182911829218293182941829518296182971829818299183001830118302183031830418305183061830718308183091831018311183121831318314183151831618317183181831918320183211832218323183241832518326183271832818329183301833118332183331833418335183361833718338183391834018341183421834318344183451834618347183481834918350183511835218353183541835518356183571835818359183601836118362183631836418365183661836718368183691837018371183721837318374183751837618377183781837918380183811838218383183841838518386183871838818389183901839118392183931839418395183961839718398183991840018401184021840318404184051840618407184081840918410184111841218413184141841518416184171841818419184201842118422184231842418425184261842718428184291843018431184321843318434184351843618437184381843918440184411844218443184441844518446184471844818449184501845118452184531845418455184561845718458184591846018461184621846318464184651846618467184681846918470184711847218473184741847518476184771847818479184801848118482184831848418485184861848718488184891849018491184921849318494184951849618497184981849918500185011850218503185041850518506185071850818509185101851118512185131851418515185161851718518185191852018521185221852318524185251852618527185281852918530185311853218533185341853518536185371853818539185401854118542185431854418545185461854718548185491855018551185521855318554185551855618557185581855918560185611856218563185641856518566185671856818569185701857118572185731857418575185761857718578185791858018581185821858318584185851858618587185881858918590185911859218593185941859518596185971859818599186001860118602186031860418605186061860718608186091861018611186121861318614186151861618617186181861918620186211862218623186241862518626186271862818629186301863118632186331863418635186361863718638186391864018641186421864318644186451864618647186481864918650186511865218653186541865518656186571865818659186601866118662186631866418665186661866718668186691867018671186721867318674186751867618677186781867918680186811868218683186841868518686186871868818689186901869118692186931869418695186961869718698186991870018701187021870318704187051870618707187081870918710187111871218713187141871518716187171871818719187201872118722187231872418725187261872718728187291873018731187321873318734187351873618737187381873918740187411874218743187441874518746187471874818749187501875118752187531875418755187561875718758187591876018761187621876318764187651876618767187681876918770187711877218773187741877518776187771877818779187801878118782187831878418785187861878718788187891879018791187921879318794187951879618797187981879918800188011880218803188041880518806188071880818809188101881118812188131881418815188161881718818188191882018821188221882318824188251882618827188281882918830188311883218833188341883518836188371883818839188401884118842188431884418845188461884718848188491885018851188521885318854188551885618857188581885918860188611886218863188641886518866188671886818869188701887118872188731887418875188761887718878188791888018881188821888318884188851888618887188881888918890188911889218893188941889518896188971889818899189001890118902189031890418905189061890718908189091891018911189121891318914189151891618917189181891918920189211892218923189241892518926189271892818929189301893118932189331893418935189361893718938189391894018941189421894318944189451894618947189481894918950189511895218953189541895518956189571895818959189601896118962189631896418965189661896718968189691897018971189721897318974189751897618977189781897918980189811898218983189841898518986189871898818989189901899118992189931899418995189961899718998189991900019001190021900319004190051900619007190081900919010190111901219013190141901519016190171901819019190201902119022190231902419025190261902719028190291903019031190321903319034190351903619037190381903919040190411904219043190441904519046190471904819049190501905119052190531905419055190561905719058190591906019061190621906319064190651906619067190681906919070190711907219073190741907519076190771907819079190801908119082190831908419085190861908719088190891909019091190921909319094190951909619097190981909919100191011910219103191041910519106191071910819109191101911119112191131911419115191161911719118191191912019121191221912319124191251912619127191281912919130191311913219133191341913519136191371913819139191401914119142191431914419145191461914719148191491915019151191521915319154191551915619157191581915919160191611916219163191641916519166191671916819169191701917119172191731917419175191761917719178191791918019181191821918319184191851918619187191881918919190191911919219193191941919519196191971919819199192001920119202192031920419205192061920719208192091921019211192121921319214192151921619217192181921919220192211922219223192241922519226192271922819229192301923119232192331923419235192361923719238192391924019241192421924319244192451924619247192481924919250192511925219253192541925519256192571925819259192601926119262192631926419265192661926719268192691927019271192721927319274192751927619277192781927919280192811928219283192841928519286192871928819289192901929119292192931929419295192961929719298192991930019301193021930319304193051930619307193081930919310193111931219313193141931519316193171931819319193201932119322193231932419325193261932719328193291933019331193321933319334193351933619337193381933919340193411934219343193441934519346193471934819349193501935119352193531935419355193561935719358193591936019361193621936319364193651936619367193681936919370193711937219373193741937519376193771937819379193801938119382193831938419385193861938719388193891939019391193921939319394193951939619397193981939919400194011940219403194041940519406194071940819409194101941119412194131941419415194161941719418194191942019421194221942319424194251942619427194281942919430194311943219433194341943519436194371943819439194401944119442194431944419445194461944719448194491945019451194521945319454194551945619457194581945919460194611946219463194641946519466194671946819469194701947119472194731947419475194761947719478194791948019481194821948319484194851948619487194881948919490194911949219493194941949519496194971949819499195001950119502195031950419505195061950719508195091951019511195121951319514195151951619517195181951919520195211952219523195241952519526195271952819529195301953119532195331953419535195361953719538195391954019541195421954319544195451954619547195481954919550195511955219553195541955519556195571955819559195601956119562195631956419565195661956719568195691957019571195721957319574195751957619577195781957919580195811958219583195841958519586195871958819589195901959119592195931959419595195961959719598195991960019601196021960319604196051960619607196081960919610196111961219613196141961519616196171961819619196201962119622196231962419625196261962719628196291963019631196321963319634196351963619637196381963919640196411964219643196441964519646196471964819649196501965119652196531965419655196561965719658196591966019661196621966319664196651966619667196681966919670196711967219673196741967519676196771967819679196801968119682196831968419685196861968719688196891969019691196921969319694196951969619697196981969919700197011970219703197041970519706197071970819709197101971119712197131971419715197161971719718197191972019721197221972319724197251972619727197281972919730197311973219733197341973519736197371973819739197401974119742197431974419745197461974719748197491975019751197521975319754197551975619757197581975919760197611976219763197641976519766197671976819769197701977119772197731977419775197761977719778197791978019781197821978319784197851978619787197881978919790197911979219793197941979519796197971979819799198001980119802198031980419805198061980719808198091981019811198121981319814198151981619817198181981919820198211982219823198241982519826198271982819829198301983119832198331983419835198361983719838198391984019841198421984319844198451984619847198481984919850198511985219853198541985519856198571985819859198601986119862198631986419865198661986719868198691987019871198721987319874198751987619877198781987919880198811988219883198841988519886198871988819889198901989119892198931989419895198961989719898198991990019901199021990319904199051990619907199081990919910199111991219913199141991519916199171991819919199201992119922199231992419925199261992719928199291993019931199321993319934199351993619937199381993919940199411994219943199441994519946199471994819949199501995119952199531995419955199561995719958199591996019961199621996319964199651996619967199681996919970199711997219973199741997519976199771997819979199801998119982199831998419985199861998719988199891999019991199921999319994199951999619997199981999920000200012000220003200042000520006200072000820009200102001120012200132001420015200162001720018200192002020021200222002320024200252002620027200282002920030200312003220033200342003520036200372003820039200402004120042200432004420045200462004720048200492005020051200522005320054200552005620057200582005920060200612006220063200642006520066200672006820069200702007120072200732007420075200762007720078200792008020081200822008320084200852008620087200882008920090200912009220093200942009520096200972009820099201002010120102201032010420105201062010720108201092011020111201122011320114201152011620117201182011920120201212012220123201242012520126201272012820129201302013120132201332013420135201362013720138201392014020141201422014320144201452014620147201482014920150201512015220153201542015520156201572015820159201602016120162201632016420165201662016720168201692017020171201722017320174201752017620177201782017920180201812018220183201842018520186201872018820189201902019120192201932019420195201962019720198201992020020201202022020320204202052020620207202082020920210202112021220213202142021520216202172021820219202202022120222202232022420225202262022720228202292023020231202322023320234202352023620237202382023920240202412024220243202442024520246202472024820249202502025120252202532025420255202562025720258202592026020261202622026320264202652026620267202682026920270202712027220273202742027520276202772027820279202802028120282202832028420285202862028720288202892029020291202922029320294202952029620297202982029920300203012030220303203042030520306203072030820309203102031120312203132031420315203162031720318203192032020321203222032320324203252032620327203282032920330203312033220333203342033520336203372033820339203402034120342203432034420345203462034720348203492035020351203522035320354203552035620357203582035920360203612036220363203642036520366203672036820369203702037120372203732037420375203762037720378203792038020381203822038320384203852038620387203882038920390203912039220393203942039520396203972039820399204002040120402204032040420405204062040720408204092041020411204122041320414204152041620417204182041920420204212042220423204242042520426204272042820429204302043120432204332043420435204362043720438204392044020441204422044320444204452044620447204482044920450204512045220453204542045520456204572045820459204602046120462204632046420465204662046720468204692047020471204722047320474204752047620477204782047920480204812048220483204842048520486204872048820489204902049120492204932049420495204962049720498204992050020501205022050320504205052050620507205082050920510205112051220513205142051520516205172051820519205202052120522205232052420525205262052720528205292053020531205322053320534205352053620537205382053920540205412054220543205442054520546205472054820549205502055120552205532055420555205562055720558205592056020561205622056320564205652056620567205682056920570205712057220573205742057520576205772057820579205802058120582205832058420585205862058720588205892059020591205922059320594205952059620597205982059920600206012060220603206042060520606206072060820609206102061120612206132061420615206162061720618206192062020621206222062320624206252062620627206282062920630206312063220633206342063520636206372063820639206402064120642206432064420645206462064720648206492065020651206522065320654206552065620657206582065920660206612066220663206642066520666206672066820669206702067120672206732067420675206762067720678206792068020681206822068320684206852068620687206882068920690206912069220693206942069520696206972069820699207002070120702207032070420705207062070720708207092071020711207122071320714207152071620717207182071920720207212072220723207242072520726207272072820729207302073120732207332073420735207362073720738207392074020741207422074320744207452074620747207482074920750207512075220753207542075520756207572075820759207602076120762207632076420765207662076720768207692077020771207722077320774207752077620777207782077920780207812078220783207842078520786207872078820789207902079120792207932079420795207962079720798207992080020801208022080320804208052080620807208082080920810208112081220813208142081520816208172081820819208202082120822208232082420825208262082720828208292083020831208322083320834208352083620837208382083920840208412084220843208442084520846208472084820849208502085120852208532085420855208562085720858208592086020861208622086320864208652086620867208682086920870208712087220873208742087520876208772087820879208802088120882208832088420885208862088720888208892089020891208922089320894208952089620897208982089920900209012090220903209042090520906209072090820909209102091120912209132091420915209162091720918209192092020921209222092320924209252092620927209282092920930209312093220933209342093520936209372093820939209402094120942209432094420945209462094720948209492095020951209522095320954209552095620957209582095920960209612096220963209642096520966209672096820969209702097120972209732097420975209762097720978209792098020981209822098320984209852098620987209882098920990209912099220993209942099520996209972099820999210002100121002210032100421005210062100721008210092101021011210122101321014210152101621017210182101921020210212102221023210242102521026210272102821029210302103121032210332103421035210362103721038210392104021041210422104321044210452104621047210482104921050210512105221053210542105521056210572105821059210602106121062210632106421065210662106721068210692107021071210722107321074210752107621077210782107921080210812108221083210842108521086210872108821089210902109121092210932109421095210962109721098210992110021101211022110321104211052110621107211082110921110211112111221113211142111521116211172111821119211202112121122211232112421125211262112721128211292113021131211322113321134211352113621137211382113921140211412114221143211442114521146211472114821149211502115121152211532115421155211562115721158211592116021161211622116321164211652116621167211682116921170211712117221173211742117521176211772117821179211802118121182211832118421185211862118721188211892119021191211922119321194211952119621197211982119921200212012120221203212042120521206212072120821209212102121121212212132121421215212162121721218212192122021221212222122321224212252122621227212282122921230212312123221233212342123521236212372123821239212402124121242212432124421245212462124721248212492125021251212522125321254212552125621257212582125921260212612126221263212642126521266212672126821269212702127121272212732127421275212762127721278212792128021281212822128321284212852128621287212882128921290212912129221293212942129521296212972129821299213002130121302213032130421305213062130721308213092131021311213122131321314213152131621317213182131921320213212132221323213242132521326213272132821329213302133121332213332133421335213362133721338213392134021341213422134321344213452134621347213482134921350213512135221353213542135521356213572135821359213602136121362213632136421365213662136721368213692137021371213722137321374213752137621377213782137921380213812138221383213842138521386213872138821389213902139121392213932139421395213962139721398213992140021401214022140321404214052140621407214082140921410214112141221413214142141521416214172141821419214202142121422214232142421425214262142721428214292143021431214322143321434214352143621437214382143921440214412144221443214442144521446214472144821449214502145121452214532145421455214562145721458214592146021461214622146321464214652146621467214682146921470214712147221473214742147521476214772147821479214802148121482214832148421485214862148721488214892149021491214922149321494214952149621497214982149921500215012150221503215042150521506215072150821509215102151121512215132151421515215162151721518215192152021521215222152321524215252152621527215282152921530215312153221533215342153521536215372153821539215402154121542215432154421545215462154721548215492155021551215522155321554215552155621557215582155921560215612156221563215642156521566215672156821569215702157121572215732157421575215762157721578215792158021581215822158321584215852158621587215882158921590215912159221593215942159521596215972159821599216002160121602216032160421605216062160721608216092161021611216122161321614216152161621617216182161921620216212162221623216242162521626216272162821629216302163121632216332163421635216362163721638216392164021641216422164321644216452164621647216482164921650216512165221653216542165521656216572165821659216602166121662216632166421665216662166721668216692167021671216722167321674216752167621677216782167921680216812168221683216842168521686216872168821689216902169121692216932169421695216962169721698216992170021701217022170321704217052170621707217082170921710217112171221713217142171521716217172171821719217202172121722217232172421725217262172721728217292173021731217322173321734217352173621737217382173921740217412174221743217442174521746217472174821749217502175121752217532175421755217562175721758217592176021761217622176321764217652176621767217682176921770217712177221773217742177521776217772177821779217802178121782217832178421785217862178721788217892179021791217922179321794217952179621797217982179921800218012180221803218042180521806218072180821809218102181121812218132181421815218162181721818218192182021821218222182321824218252182621827218282182921830218312183221833218342183521836218372183821839218402184121842218432184421845218462184721848218492185021851218522185321854218552185621857218582185921860218612186221863218642186521866218672186821869218702187121872218732187421875218762187721878218792188021881218822188321884218852188621887218882188921890218912189221893218942189521896218972189821899219002190121902219032190421905219062190721908219092191021911219122191321914219152191621917219182191921920219212192221923219242192521926219272192821929219302193121932219332193421935219362193721938219392194021941219422194321944219452194621947219482194921950219512195221953219542195521956219572195821959219602196121962219632196421965219662196721968219692197021971219722197321974219752197621977219782197921980219812198221983219842198521986219872198821989219902199121992219932199421995219962199721998219992200022001220022200322004220052200622007220082200922010220112201222013220142201522016220172201822019220202202122022220232202422025220262202722028220292203022031220322203322034220352203622037220382203922040220412204222043220442204522046220472204822049220502205122052220532205422055220562205722058220592206022061220622206322064220652206622067220682206922070220712207222073220742207522076220772207822079220802208122082220832208422085220862208722088220892209022091220922209322094220952209622097220982209922100221012210222103221042210522106221072210822109221102211122112221132211422115221162211722118221192212022121221222212322124221252212622127221282212922130221312213222133221342213522136221372213822139221402214122142221432214422145221462214722148221492215022151221522215322154221552215622157221582215922160221612216222163221642216522166221672216822169221702217122172221732217422175221762217722178221792218022181221822218322184221852218622187221882218922190221912219222193221942219522196221972219822199222002220122202222032220422205222062220722208222092221022211222122221322214222152221622217222182221922220222212222222223222242222522226222272222822229222302223122232222332223422235222362223722238222392224022241222422224322244222452224622247222482224922250222512225222253222542225522256222572225822259222602226122262222632226422265222662226722268222692227022271222722227322274222752227622277222782227922280222812228222283222842228522286222872228822289222902229122292222932229422295222962229722298222992230022301223022230322304223052230622307223082230922310223112231222313223142231522316223172231822319223202232122322223232232422325223262232722328223292233022331223322233322334223352233622337223382233922340223412234222343223442234522346223472234822349223502235122352223532235422355223562235722358223592236022361223622236322364223652236622367223682236922370223712237222373223742237522376223772237822379223802238122382223832238422385223862238722388223892239022391223922239322394223952239622397223982239922400224012240222403224042240522406224072240822409224102241122412224132241422415224162241722418224192242022421224222242322424224252242622427224282242922430224312243222433224342243522436224372243822439224402244122442224432244422445224462244722448224492245022451224522245322454224552245622457224582245922460224612246222463224642246522466224672246822469224702247122472224732247422475224762247722478224792248022481224822248322484224852248622487224882248922490224912249222493224942249522496224972249822499225002250122502225032250422505225062250722508225092251022511225122251322514225152251622517225182251922520225212252222523225242252522526225272252822529225302253122532225332253422535225362253722538225392254022541225422254322544225452254622547225482254922550225512255222553225542255522556225572255822559225602256122562225632256422565225662256722568225692257022571225722257322574225752257622577225782257922580225812258222583225842258522586225872258822589225902259122592225932259422595225962259722598225992260022601226022260322604226052260622607226082260922610226112261222613226142261522616226172261822619226202262122622226232262422625226262262722628226292263022631226322263322634226352263622637226382263922640226412264222643226442264522646226472264822649226502265122652226532265422655226562265722658226592266022661226622266322664226652266622667226682266922670226712267222673226742267522676226772267822679226802268122682226832268422685226862268722688226892269022691226922269322694226952269622697226982269922700227012270222703227042270522706227072270822709227102271122712227132271422715227162271722718227192272022721227222272322724227252272622727227282272922730227312273222733227342273522736227372273822739227402274122742227432274422745227462274722748227492275022751227522275322754227552275622757227582275922760227612276222763227642276522766227672276822769227702277122772227732277422775227762277722778227792278022781227822278322784227852278622787227882278922790227912279222793227942279522796227972279822799228002280122802228032280422805228062280722808228092281022811228122281322814228152281622817228182281922820228212282222823228242282522826228272282822829228302283122832228332283422835228362283722838228392284022841228422284322844228452284622847228482284922850228512285222853228542285522856228572285822859228602286122862228632286422865228662286722868228692287022871228722287322874228752287622877228782287922880228812288222883228842288522886228872288822889228902289122892228932289422895228962289722898228992290022901229022290322904229052290622907229082290922910229112291222913229142291522916229172291822919229202292122922229232292422925229262292722928229292293022931229322293322934229352293622937229382293922940229412294222943229442294522946229472294822949229502295122952229532295422955229562295722958229592296022961229622296322964229652296622967229682296922970229712297222973229742297522976229772297822979229802298122982229832298422985229862298722988229892299022991229922299322994229952299622997229982299923000230012300223003230042300523006230072300823009230102301123012230132301423015230162301723018230192302023021230222302323024230252302623027230282302923030230312303223033230342303523036230372303823039230402304123042230432304423045230462304723048230492305023051230522305323054230552305623057230582305923060230612306223063230642306523066230672306823069230702307123072230732307423075230762307723078230792308023081230822308323084230852308623087230882308923090230912309223093230942309523096230972309823099231002310123102231032310423105231062310723108231092311023111231122311323114231152311623117231182311923120231212312223123231242312523126231272312823129231302313123132231332313423135231362313723138231392314023141231422314323144231452314623147231482314923150231512315223153231542315523156231572315823159231602316123162231632316423165231662316723168231692317023171231722317323174231752317623177231782317923180231812318223183231842318523186231872318823189231902319123192231932319423195231962319723198231992320023201232022320323204232052320623207232082320923210232112321223213232142321523216232172321823219232202322123222232232322423225232262322723228232292323023231232322323323234232352323623237232382323923240232412324223243232442324523246232472324823249232502325123252232532325423255232562325723258232592326023261232622326323264232652326623267232682326923270232712327223273232742327523276232772327823279232802328123282232832328423285232862328723288232892329023291232922329323294232952329623297232982329923300233012330223303233042330523306233072330823309233102331123312233132331423315233162331723318233192332023321233222332323324233252332623327233282332923330233312333223333233342333523336233372333823339233402334123342233432334423345233462334723348233492335023351233522335323354233552335623357233582335923360233612336223363233642336523366233672336823369233702337123372233732337423375233762337723378233792338023381233822338323384233852338623387233882338923390233912339223393233942339523396233972339823399234002340123402234032340423405234062340723408234092341023411234122341323414234152341623417234182341923420234212342223423234242342523426234272342823429234302343123432234332343423435234362343723438234392344023441234422344323444234452344623447234482344923450234512345223453234542345523456234572345823459234602346123462234632346423465234662346723468234692347023471234722347323474234752347623477234782347923480234812348223483234842348523486234872348823489234902349123492234932349423495234962349723498234992350023501235022350323504235052350623507235082350923510235112351223513235142351523516235172351823519235202352123522235232352423525235262352723528235292353023531235322353323534235352353623537235382353923540235412354223543235442354523546235472354823549235502355123552235532355423555235562355723558235592356023561235622356323564235652356623567235682356923570235712357223573235742357523576235772357823579235802358123582235832358423585235862358723588235892359023591235922359323594235952359623597235982359923600236012360223603236042360523606236072360823609236102361123612236132361423615236162361723618236192362023621236222362323624236252362623627236282362923630236312363223633236342363523636236372363823639236402364123642236432364423645236462364723648236492365023651236522365323654236552365623657236582365923660236612366223663236642366523666236672366823669236702367123672236732367423675236762367723678236792368023681236822368323684236852368623687236882368923690236912369223693236942369523696236972369823699237002370123702237032370423705237062370723708237092371023711237122371323714237152371623717237182371923720237212372223723237242372523726237272372823729237302373123732237332373423735237362373723738237392374023741237422374323744237452374623747237482374923750237512375223753237542375523756237572375823759237602376123762237632376423765237662376723768237692377023771237722377323774237752377623777237782377923780237812378223783237842378523786237872378823789237902379123792237932379423795237962379723798237992380023801238022380323804238052380623807238082380923810238112381223813238142381523816238172381823819238202382123822238232382423825238262382723828238292383023831238322383323834238352383623837238382383923840238412384223843238442384523846238472384823849238502385123852238532385423855238562385723858238592386023861238622386323864238652386623867238682386923870238712387223873238742387523876238772387823879238802388123882238832388423885238862388723888238892389023891238922389323894238952389623897238982389923900239012390223903239042390523906239072390823909239102391123912239132391423915239162391723918239192392023921239222392323924239252392623927239282392923930239312393223933239342393523936239372393823939239402394123942239432394423945239462394723948239492395023951239522395323954239552395623957239582395923960239612396223963239642396523966239672396823969239702397123972239732397423975239762397723978239792398023981239822398323984239852398623987239882398923990239912399223993239942399523996239972399823999240002400124002240032400424005240062400724008240092401024011240122401324014240152401624017240182401924020240212402224023240242402524026240272402824029240302403124032240332403424035240362403724038240392404024041240422404324044240452404624047240482404924050240512405224053240542405524056240572405824059240602406124062240632406424065240662406724068240692407024071240722407324074240752407624077240782407924080240812408224083240842408524086240872408824089240902409124092240932409424095240962409724098240992410024101241022410324104241052410624107241082410924110241112411224113241142411524116241172411824119241202412124122241232412424125241262412724128241292413024131241322413324134241352413624137241382413924140241412414224143241442414524146241472414824149241502415124152241532415424155241562415724158241592416024161241622416324164241652416624167241682416924170241712417224173241742417524176241772417824179241802418124182241832418424185241862418724188241892419024191241922419324194241952419624197241982419924200242012420224203242042420524206242072420824209242102421124212242132421424215242162421724218242192422024221242222422324224242252422624227242282422924230242312423224233242342423524236242372423824239242402424124242242432424424245242462424724248242492425024251242522425324254242552425624257242582425924260242612426224263242642426524266242672426824269242702427124272242732427424275242762427724278242792428024281242822428324284242852428624287242882428924290242912429224293242942429524296242972429824299243002430124302243032430424305243062430724308243092431024311243122431324314243152431624317243182431924320243212432224323243242432524326243272432824329243302433124332243332433424335243362433724338243392434024341243422434324344243452434624347243482434924350243512435224353243542435524356243572435824359243602436124362243632436424365243662436724368243692437024371243722437324374243752437624377243782437924380243812438224383243842438524386243872438824389243902439124392243932439424395243962439724398243992440024401244022440324404244052440624407244082440924410244112441224413244142441524416244172441824419244202442124422244232442424425244262442724428244292443024431244322443324434244352443624437244382443924440244412444224443244442444524446244472444824449244502445124452244532445424455244562445724458244592446024461244622446324464244652446624467244682446924470244712447224473244742447524476244772447824479244802448124482244832448424485244862448724488244892449024491244922449324494244952449624497244982449924500245012450224503245042450524506245072450824509245102451124512245132451424515245162451724518245192452024521245222452324524245252452624527245282452924530245312453224533245342453524536245372453824539245402454124542245432454424545245462454724548245492455024551245522455324554245552455624557245582455924560245612456224563245642456524566245672456824569245702457124572245732457424575245762457724578245792458024581245822458324584245852458624587245882458924590245912459224593245942459524596245972459824599246002460124602246032460424605246062460724608246092461024611246122461324614246152461624617246182461924620246212462224623246242462524626246272462824629246302463124632246332463424635246362463724638246392464024641246422464324644246452464624647246482464924650246512465224653246542465524656246572465824659246602466124662246632466424665246662466724668246692467024671246722467324674246752467624677246782467924680246812468224683246842468524686246872468824689246902469124692246932469424695246962469724698246992470024701247022470324704247052470624707247082470924710247112471224713247142471524716247172471824719247202472124722247232472424725247262472724728247292473024731247322473324734247352473624737247382473924740247412474224743247442474524746247472474824749247502475124752247532475424755247562475724758247592476024761247622476324764247652476624767247682476924770247712477224773247742477524776247772477824779247802478124782247832478424785247862478724788247892479024791247922479324794247952479624797247982479924800248012480224803248042480524806248072480824809248102481124812248132481424815248162481724818248192482024821248222482324824248252482624827248282482924830248312483224833248342483524836248372483824839248402484124842248432484424845248462484724848248492485024851248522485324854248552485624857248582485924860248612486224863248642486524866248672486824869248702487124872248732487424875248762487724878248792488024881248822488324884248852488624887248882488924890248912489224893248942489524896248972489824899249002490124902249032490424905249062490724908249092491024911249122491324914249152491624917249182491924920249212492224923249242492524926249272492824929249302493124932249332493424935249362493724938249392494024941249422494324944249452494624947249482494924950249512495224953249542495524956249572495824959249602496124962249632496424965249662496724968249692497024971249722497324974249752497624977249782497924980249812498224983249842498524986249872498824989249902499124992249932499424995249962499724998249992500025001250022500325004250052500625007250082500925010250112501225013250142501525016250172501825019250202502125022250232502425025250262502725028250292503025031250322503325034250352503625037250382503925040250412504225043250442504525046250472504825049250502505125052250532505425055250562505725058250592506025061250622506325064250652506625067250682506925070250712507225073250742507525076250772507825079250802508125082250832508425085250862508725088250892509025091250922509325094250952509625097250982509925100251012510225103251042510525106251072510825109251102511125112251132511425115251162511725118251192512025121251222512325124251252512625127251282512925130251312513225133251342513525136251372513825139251402514125142251432514425145251462514725148251492515025151251522515325154251552515625157251582515925160251612516225163251642516525166251672516825169251702517125172251732517425175251762517725178251792518025181251822518325184251852518625187251882518925190251912519225193251942519525196251972519825199252002520125202252032520425205252062520725208252092521025211252122521325214252152521625217252182521925220252212522225223252242522525226252272522825229252302523125232252332523425235252362523725238252392524025241252422524325244252452524625247252482524925250252512525225253252542525525256252572525825259252602526125262252632526425265252662526725268252692527025271252722527325274252752527625277252782527925280252812528225283252842528525286252872528825289252902529125292252932529425295252962529725298252992530025301253022530325304253052530625307253082530925310253112531225313253142531525316253172531825319253202532125322253232532425325253262532725328253292533025331253322533325334253352533625337253382533925340253412534225343253442534525346253472534825349253502535125352253532535425355253562535725358253592536025361253622536325364253652536625367253682536925370253712537225373253742537525376253772537825379253802538125382253832538425385253862538725388253892539025391253922539325394253952539625397253982539925400254012540225403254042540525406254072540825409254102541125412254132541425415254162541725418254192542025421254222542325424254252542625427254282542925430254312543225433254342543525436254372543825439254402544125442254432544425445254462544725448254492545025451254522545325454254552545625457254582545925460254612546225463254642546525466254672546825469254702547125472254732547425475254762547725478254792548025481254822548325484254852548625487254882548925490254912549225493254942549525496254972549825499255002550125502255032550425505255062550725508255092551025511255122551325514255152551625517255182551925520255212552225523255242552525526255272552825529255302553125532255332553425535255362553725538255392554025541255422554325544255452554625547255482554925550255512555225553255542555525556255572555825559255602556125562255632556425565255662556725568255692557025571255722557325574255752557625577255782557925580255812558225583255842558525586255872558825589255902559125592255932559425595255962559725598255992560025601256022560325604256052560625607256082560925610256112561225613256142561525616256172561825619256202562125622256232562425625256262562725628256292563025631256322563325634256352563625637256382563925640256412564225643256442564525646256472564825649256502565125652256532565425655256562565725658256592566025661256622566325664256652566625667256682566925670256712567225673256742567525676256772567825679256802568125682256832568425685256862568725688256892569025691256922569325694256952569625697256982569925700257012570225703257042570525706257072570825709257102571125712257132571425715257162571725718257192572025721257222572325724257252572625727257282572925730257312573225733257342573525736257372573825739257402574125742257432574425745257462574725748257492575025751257522575325754257552575625757257582575925760257612576225763257642576525766257672576825769257702577125772257732577425775257762577725778257792578025781257822578325784257852578625787257882578925790257912579225793257942579525796257972579825799258002580125802258032580425805258062580725808258092581025811258122581325814258152581625817258182581925820258212582225823258242582525826258272582825829258302583125832258332583425835258362583725838258392584025841258422584325844258452584625847258482584925850258512585225853258542585525856258572585825859258602586125862258632586425865258662586725868258692587025871258722587325874258752587625877258782587925880258812588225883258842588525886258872588825889258902589125892258932589425895258962589725898258992590025901259022590325904259052590625907259082590925910259112591225913259142591525916259172591825919259202592125922259232592425925259262592725928259292593025931259322593325934259352593625937259382593925940259412594225943259442594525946259472594825949259502595125952259532595425955259562595725958259592596025961259622596325964259652596625967259682596925970259712597225973259742597525976259772597825979259802598125982259832598425985259862598725988259892599025991259922599325994259952599625997259982599926000260012600226003260042600526006260072600826009260102601126012260132601426015260162601726018260192602026021260222602326024260252602626027260282602926030260312603226033260342603526036260372603826039260402604126042260432604426045260462604726048260492605026051260522605326054260552605626057260582605926060260612606226063260642606526066260672606826069260702607126072260732607426075260762607726078260792608026081260822608326084260852608626087260882608926090260912609226093260942609526096260972609826099261002610126102261032610426105261062610726108261092611026111261122611326114261152611626117261182611926120261212612226123261242612526126261272612826129261302613126132261332613426135261362613726138261392614026141261422614326144261452614626147261482614926150261512615226153261542615526156261572615826159261602616126162261632616426165261662616726168261692617026171261722617326174261752617626177261782617926180261812618226183261842618526186261872618826189261902619126192261932619426195261962619726198261992620026201262022620326204262052620626207262082620926210262112621226213262142621526216262172621826219262202622126222262232622426225262262622726228262292623026231262322623326234262352623626237262382623926240262412624226243262442624526246262472624826249262502625126252262532625426255262562625726258262592626026261262622626326264262652626626267262682626926270262712627226273262742627526276262772627826279262802628126282262832628426285262862628726288262892629026291262922629326294262952629626297262982629926300263012630226303263042630526306263072630826309263102631126312263132631426315263162631726318263192632026321263222632326324263252632626327263282632926330263312633226333263342633526336263372633826339263402634126342263432634426345263462634726348263492635026351263522635326354263552635626357263582635926360263612636226363263642636526366263672636826369263702637126372263732637426375263762637726378263792638026381263822638326384263852638626387263882638926390263912639226393263942639526396263972639826399264002640126402264032640426405264062640726408264092641026411264122641326414264152641626417264182641926420264212642226423264242642526426264272642826429264302643126432264332643426435264362643726438264392644026441264422644326444264452644626447264482644926450264512645226453264542645526456264572645826459264602646126462264632646426465264662646726468264692647026471264722647326474264752647626477264782647926480264812648226483264842648526486264872648826489264902649126492264932649426495264962649726498264992650026501265022650326504265052650626507265082650926510265112651226513265142651526516265172651826519265202652126522265232652426525265262652726528265292653026531265322653326534265352653626537265382653926540265412654226543265442654526546265472654826549265502655126552265532655426555265562655726558265592656026561265622656326564265652656626567265682656926570265712657226573265742657526576265772657826579265802658126582265832658426585265862658726588265892659026591265922659326594265952659626597265982659926600266012660226603266042660526606266072660826609266102661126612266132661426615266162661726618266192662026621266222662326624266252662626627266282662926630266312663226633266342663526636266372663826639266402664126642266432664426645266462664726648266492665026651266522665326654266552665626657266582665926660266612666226663266642666526666266672666826669266702667126672266732667426675266762667726678266792668026681266822668326684266852668626687266882668926690266912669226693266942669526696266972669826699267002670126702267032670426705267062670726708267092671026711267122671326714267152671626717267182671926720267212672226723267242672526726267272672826729267302673126732267332673426735267362673726738267392674026741267422674326744267452674626747267482674926750267512675226753267542675526756267572675826759267602676126762267632676426765267662676726768267692677026771267722677326774267752677626777267782677926780267812678226783267842678526786267872678826789267902679126792267932679426795267962679726798267992680026801268022680326804268052680626807268082680926810268112681226813268142681526816268172681826819268202682126822268232682426825268262682726828268292683026831268322683326834268352683626837268382683926840268412684226843268442684526846268472684826849268502685126852268532685426855268562685726858268592686026861268622686326864268652686626867268682686926870268712687226873268742687526876268772687826879268802688126882268832688426885268862688726888268892689026891268922689326894268952689626897268982689926900269012690226903269042690526906269072690826909269102691126912269132691426915269162691726918269192692026921269222692326924269252692626927269282692926930269312693226933269342693526936269372693826939269402694126942269432694426945269462694726948269492695026951269522695326954269552695626957269582695926960269612696226963269642696526966269672696826969269702697126972269732697426975269762697726978269792698026981269822698326984269852698626987269882698926990269912699226993269942699526996269972699826999270002700127002270032700427005270062700727008270092701027011270122701327014270152701627017270182701927020270212702227023270242702527026270272702827029270302703127032270332703427035270362703727038270392704027041270422704327044270452704627047270482704927050270512705227053270542705527056270572705827059270602706127062270632706427065270662706727068270692707027071270722707327074270752707627077270782707927080270812708227083270842708527086270872708827089270902709127092270932709427095270962709727098270992710027101271022710327104271052710627107271082710927110271112711227113271142711527116271172711827119271202712127122271232712427125271262712727128271292713027131271322713327134271352713627137271382713927140271412714227143271442714527146271472714827149271502715127152271532715427155271562715727158271592716027161271622716327164271652716627167271682716927170271712717227173271742717527176271772717827179271802718127182271832718427185271862718727188271892719027191271922719327194271952719627197271982719927200272012720227203272042720527206272072720827209272102721127212272132721427215272162721727218272192722027221272222722327224272252722627227272282722927230272312723227233272342723527236272372723827239272402724127242272432724427245272462724727248272492725027251272522725327254272552725627257272582725927260272612726227263272642726527266272672726827269272702727127272272732727427275272762727727278272792728027281272822728327284272852728627287272882728927290272912729227293272942729527296272972729827299273002730127302273032730427305273062730727308273092731027311273122731327314273152731627317273182731927320273212732227323273242732527326273272732827329273302733127332273332733427335273362733727338273392734027341273422734327344273452734627347273482734927350273512735227353273542735527356273572735827359273602736127362273632736427365273662736727368273692737027371273722737327374273752737627377273782737927380273812738227383273842738527386273872738827389273902739127392273932739427395273962739727398273992740027401274022740327404274052740627407274082740927410274112741227413274142741527416274172741827419274202742127422274232742427425274262742727428274292743027431274322743327434274352743627437274382743927440274412744227443274442744527446274472744827449274502745127452274532745427455274562745727458274592746027461274622746327464274652746627467274682746927470274712747227473274742747527476274772747827479274802748127482274832748427485274862748727488274892749027491274922749327494274952749627497274982749927500275012750227503275042750527506275072750827509275102751127512275132751427515275162751727518275192752027521275222752327524275252752627527275282752927530275312753227533275342753527536275372753827539275402754127542275432754427545275462754727548275492755027551275522755327554275552755627557275582755927560275612756227563275642756527566275672756827569275702757127572275732757427575275762757727578275792758027581275822758327584275852758627587275882758927590275912759227593275942759527596275972759827599276002760127602276032760427605276062760727608276092761027611276122761327614276152761627617276182761927620276212762227623276242762527626276272762827629276302763127632276332763427635276362763727638276392764027641276422764327644276452764627647276482764927650276512765227653276542765527656276572765827659276602766127662276632766427665276662766727668276692767027671276722767327674276752767627677276782767927680276812768227683276842768527686276872768827689276902769127692276932769427695276962769727698276992770027701277022770327704277052770627707277082770927710277112771227713277142771527716277172771827719277202772127722277232772427725277262772727728277292773027731277322773327734277352773627737277382773927740277412774227743277442774527746277472774827749277502775127752277532775427755277562775727758277592776027761277622776327764277652776627767277682776927770277712777227773277742777527776277772777827779277802778127782277832778427785277862778727788277892779027791277922779327794277952779627797277982779927800278012780227803278042780527806278072780827809278102781127812278132781427815278162781727818278192782027821278222782327824278252782627827278282782927830278312783227833278342783527836278372783827839278402784127842278432784427845278462784727848278492785027851278522785327854278552785627857278582785927860278612786227863278642786527866278672786827869278702787127872278732787427875278762787727878278792788027881278822788327884278852788627887278882788927890278912789227893278942789527896278972789827899279002790127902279032790427905279062790727908279092791027911279122791327914279152791627917279182791927920279212792227923279242792527926279272792827929279302793127932279332793427935279362793727938279392794027941279422794327944279452794627947279482794927950279512795227953279542795527956279572795827959279602796127962279632796427965279662796727968279692797027971279722797327974279752797627977279782797927980279812798227983279842798527986279872798827989279902799127992279932799427995279962799727998279992800028001280022800328004280052800628007280082800928010280112801228013280142801528016280172801828019280202802128022280232802428025280262802728028280292803028031280322803328034280352803628037280382803928040280412804228043280442804528046280472804828049280502805128052280532805428055280562805728058280592806028061280622806328064280652806628067280682806928070280712807228073280742807528076280772807828079280802808128082280832808428085280862808728088280892809028091280922809328094280952809628097280982809928100281012810228103281042810528106281072810828109281102811128112281132811428115281162811728118281192812028121281222812328124281252812628127281282812928130281312813228133281342813528136281372813828139281402814128142281432814428145281462814728148281492815028151281522815328154281552815628157281582815928160281612816228163281642816528166281672816828169281702817128172281732817428175281762817728178281792818028181281822818328184281852818628187281882818928190281912819228193281942819528196281972819828199282002820128202282032820428205282062820728208282092821028211282122821328214282152821628217282182821928220282212822228223282242822528226282272822828229282302823128232282332823428235282362823728238282392824028241282422824328244282452824628247282482824928250282512825228253282542825528256282572825828259282602826128262282632826428265282662826728268282692827028271282722827328274282752827628277282782827928280282812828228283282842828528286282872828828289282902829128292282932829428295282962829728298282992830028301283022830328304283052830628307283082830928310283112831228313283142831528316283172831828319283202832128322283232832428325283262832728328283292833028331283322833328334283352833628337283382833928340283412834228343283442834528346283472834828349283502835128352283532835428355283562835728358283592836028361283622836328364283652836628367283682836928370283712837228373283742837528376283772837828379283802838128382283832838428385283862838728388283892839028391283922839328394283952839628397283982839928400284012840228403284042840528406284072840828409284102841128412284132841428415284162841728418284192842028421284222842328424284252842628427284282842928430284312843228433284342843528436284372843828439284402844128442284432844428445284462844728448284492845028451284522845328454284552845628457284582845928460284612846228463284642846528466284672846828469284702847128472284732847428475284762847728478284792848028481284822848328484284852848628487284882848928490284912849228493284942849528496284972849828499285002850128502285032850428505285062850728508285092851028511285122851328514285152851628517285182851928520285212852228523285242852528526285272852828529285302853128532285332853428535285362853728538285392854028541285422854328544285452854628547285482854928550285512855228553285542855528556285572855828559285602856128562285632856428565285662856728568285692857028571285722857328574285752857628577285782857928580285812858228583285842858528586285872858828589285902859128592285932859428595285962859728598285992860028601286022860328604286052860628607286082860928610286112861228613286142861528616286172861828619286202862128622286232862428625286262862728628286292863028631286322863328634286352863628637286382863928640286412864228643286442864528646286472864828649286502865128652286532865428655286562865728658286592866028661286622866328664286652866628667286682866928670286712867228673286742867528676286772867828679286802868128682286832868428685286862868728688286892869028691286922869328694286952869628697286982869928700287012870228703287042870528706287072870828709287102871128712287132871428715287162871728718287192872028721287222872328724287252872628727287282872928730287312873228733287342873528736287372873828739287402874128742287432874428745287462874728748287492875028751287522875328754287552875628757287582875928760287612876228763287642876528766287672876828769287702877128772287732877428775287762877728778287792878028781287822878328784287852878628787287882878928790287912879228793287942879528796287972879828799288002880128802288032880428805288062880728808288092881028811288122881328814288152881628817288182881928820288212882228823288242882528826288272882828829288302883128832288332883428835288362883728838288392884028841288422884328844288452884628847288482884928850288512885228853288542885528856288572885828859288602886128862288632886428865288662886728868288692887028871288722887328874288752887628877288782887928880288812888228883288842888528886288872888828889288902889128892288932889428895288962889728898288992890028901289022890328904289052890628907289082890928910289112891228913289142891528916289172891828919289202892128922289232892428925289262892728928289292893028931289322893328934289352893628937289382893928940289412894228943289442894528946289472894828949289502895128952289532895428955289562895728958289592896028961289622896328964289652896628967289682896928970289712897228973289742897528976289772897828979289802898128982289832898428985289862898728988289892899028991289922899328994289952899628997289982899929000290012900229003290042900529006290072900829009290102901129012290132901429015290162901729018290192902029021290222902329024290252902629027290282902929030290312903229033290342903529036290372903829039290402904129042290432904429045290462904729048290492905029051290522905329054290552905629057290582905929060290612906229063290642906529066290672906829069290702907129072290732907429075290762907729078290792908029081290822908329084290852908629087290882908929090290912909229093290942909529096290972909829099291002910129102291032910429105291062910729108291092911029111291122911329114291152911629117291182911929120291212912229123291242912529126291272912829129291302913129132291332913429135291362913729138291392914029141291422914329144291452914629147291482914929150291512915229153291542915529156291572915829159291602916129162291632916429165291662916729168291692917029171291722917329174291752917629177291782917929180291812918229183291842918529186291872918829189291902919129192291932919429195291962919729198291992920029201292022920329204292052920629207292082920929210292112921229213292142921529216292172921829219292202922129222292232922429225292262922729228292292923029231292322923329234292352923629237292382923929240292412924229243292442924529246292472924829249292502925129252292532925429255292562925729258292592926029261292622926329264292652926629267292682926929270292712927229273292742927529276292772927829279292802928129282292832928429285292862928729288292892929029291292922929329294292952929629297292982929929300293012930229303293042930529306293072930829309293102931129312293132931429315293162931729318293192932029321293222932329324293252932629327293282932929330293312933229333293342933529336293372933829339293402934129342293432934429345293462934729348293492935029351293522935329354293552935629357293582935929360293612936229363293642936529366293672936829369293702937129372293732937429375293762937729378293792938029381293822938329384293852938629387293882938929390293912939229393293942939529396293972939829399294002940129402294032940429405294062940729408294092941029411294122941329414294152941629417294182941929420294212942229423294242942529426294272942829429294302943129432294332943429435294362943729438294392944029441294422944329444294452944629447294482944929450294512945229453294542945529456294572945829459294602946129462294632946429465294662946729468294692947029471294722947329474294752947629477294782947929480294812948229483294842948529486294872948829489294902949129492294932949429495294962949729498294992950029501295022950329504295052950629507295082950929510295112951229513295142951529516295172951829519295202952129522295232952429525295262952729528295292953029531295322953329534295352953629537295382953929540295412954229543295442954529546295472954829549295502955129552295532955429555295562955729558295592956029561295622956329564295652956629567295682956929570295712957229573295742957529576295772957829579295802958129582295832958429585295862958729588295892959029591295922959329594295952959629597295982959929600296012960229603296042960529606296072960829609296102961129612296132961429615296162961729618296192962029621296222962329624296252962629627296282962929630296312963229633296342963529636296372963829639296402964129642296432964429645296462964729648296492965029651296522965329654296552965629657296582965929660296612966229663296642966529666296672966829669296702967129672296732967429675296762967729678296792968029681296822968329684296852968629687296882968929690296912969229693296942969529696296972969829699297002970129702297032970429705297062970729708297092971029711297122971329714297152971629717297182971929720297212972229723297242972529726297272972829729297302973129732297332973429735297362973729738297392974029741297422974329744297452974629747297482974929750297512975229753297542975529756297572975829759297602976129762297632976429765297662976729768297692977029771297722977329774297752977629777297782977929780297812978229783297842978529786297872978829789297902979129792297932979429795297962979729798297992980029801298022980329804298052980629807298082980929810298112981229813298142981529816298172981829819298202982129822298232982429825298262982729828298292983029831298322983329834298352983629837298382983929840298412984229843298442984529846298472984829849298502985129852298532985429855298562985729858298592986029861298622986329864298652986629867298682986929870298712987229873298742987529876298772987829879298802988129882298832988429885298862988729888298892989029891298922989329894298952989629897298982989929900299012990229903299042990529906299072990829909299102991129912299132991429915299162991729918299192992029921299222992329924299252992629927299282992929930299312993229933299342993529936299372993829939299402994129942299432994429945299462994729948299492995029951299522995329954299552995629957299582995929960299612996229963299642996529966299672996829969299702997129972299732997429975299762997729978299792998029981299822998329984299852998629987299882998929990299912999229993299942999529996299972999829999300003000130002300033000430005300063000730008300093001030011300123001330014300153001630017300183001930020300213002230023300243002530026300273002830029300303003130032300333003430035300363003730038300393004030041300423004330044300453004630047300483004930050300513005230053300543005530056300573005830059300603006130062300633006430065300663006730068300693007030071300723007330074300753007630077300783007930080300813008230083300843008530086300873008830089300903009130092300933009430095300963009730098300993010030101301023010330104301053010630107301083010930110301113011230113301143011530116301173011830119301203012130122301233012430125301263012730128301293013030131301323013330134301353013630137301383013930140301413014230143301443014530146301473014830149301503015130152301533015430155301563015730158301593016030161301623016330164301653016630167301683016930170301713017230173301743017530176301773017830179301803018130182301833018430185301863018730188301893019030191301923019330194301953019630197301983019930200302013020230203302043020530206302073020830209302103021130212302133021430215302163021730218302193022030221302223022330224302253022630227302283022930230302313023230233302343023530236302373023830239302403024130242302433024430245302463024730248302493025030251302523025330254302553025630257302583025930260302613026230263302643026530266302673026830269302703027130272302733027430275302763027730278302793028030281302823028330284302853028630287302883028930290302913029230293302943029530296302973029830299303003030130302303033030430305303063030730308303093031030311303123031330314303153031630317303183031930320303213032230323303243032530326303273032830329303303033130332303333033430335303363033730338303393034030341303423034330344303453034630347303483034930350303513035230353303543035530356303573035830359303603036130362303633036430365303663036730368303693037030371303723037330374303753037630377303783037930380303813038230383303843038530386303873038830389303903039130392303933039430395303963039730398303993040030401304023040330404304053040630407304083040930410304113041230413304143041530416304173041830419304203042130422304233042430425304263042730428304293043030431304323043330434304353043630437304383043930440304413044230443304443044530446304473044830449304503045130452304533045430455304563045730458304593046030461304623046330464304653046630467304683046930470304713047230473304743047530476304773047830479304803048130482304833048430485304863048730488304893049030491304923049330494304953049630497304983049930500305013050230503305043050530506305073050830509305103051130512305133051430515305163051730518305193052030521305223052330524305253052630527305283052930530305313053230533305343053530536305373053830539305403054130542305433054430545305463054730548305493055030551305523055330554305553055630557305583055930560305613056230563305643056530566305673056830569305703057130572305733057430575305763057730578305793058030581305823058330584305853058630587305883058930590305913059230593305943059530596305973059830599306003060130602306033060430605306063060730608306093061030611306123061330614306153061630617306183061930620306213062230623306243062530626306273062830629306303063130632306333063430635306363063730638306393064030641306423064330644306453064630647306483064930650306513065230653306543065530656306573065830659306603066130662306633066430665306663066730668306693067030671306723067330674306753067630677306783067930680306813068230683306843068530686306873068830689306903069130692306933069430695306963069730698306993070030701307023070330704307053070630707307083070930710307113071230713307143071530716307173071830719307203072130722307233072430725307263072730728307293073030731307323073330734307353073630737307383073930740307413074230743307443074530746307473074830749307503075130752307533075430755307563075730758307593076030761307623076330764307653076630767307683076930770307713077230773307743077530776307773077830779307803078130782307833078430785307863078730788307893079030791307923079330794307953079630797307983079930800308013080230803308043080530806308073080830809308103081130812308133081430815308163081730818308193082030821308223082330824308253082630827308283082930830308313083230833308343083530836308373083830839308403084130842308433084430845308463084730848308493085030851308523085330854308553085630857308583085930860308613086230863308643086530866308673086830869308703087130872308733087430875308763087730878308793088030881308823088330884308853088630887308883088930890308913089230893308943089530896308973089830899309003090130902309033090430905309063090730908309093091030911309123091330914309153091630917309183091930920309213092230923309243092530926309273092830929309303093130932309333093430935309363093730938309393094030941309423094330944309453094630947309483094930950309513095230953309543095530956309573095830959309603096130962309633096430965309663096730968309693097030971309723097330974309753097630977309783097930980309813098230983309843098530986309873098830989309903099130992309933099430995309963099730998309993100031001310023100331004310053100631007310083100931010310113101231013310143101531016310173101831019310203102131022310233102431025310263102731028310293103031031310323103331034310353103631037310383103931040310413104231043310443104531046310473104831049310503105131052310533105431055310563105731058310593106031061310623106331064310653106631067310683106931070310713107231073310743107531076310773107831079310803108131082310833108431085310863108731088310893109031091310923109331094310953109631097310983109931100311013110231103311043110531106311073110831109311103111131112311133111431115311163111731118311193112031121311223112331124311253112631127311283112931130311313113231133311343113531136311373113831139311403114131142311433114431145311463114731148311493115031151311523115331154311553115631157311583115931160311613116231163311643116531166311673116831169311703117131172311733117431175311763117731178311793118031181311823118331184311853118631187311883118931190311913119231193311943119531196311973119831199312003120131202312033120431205312063120731208312093121031211312123121331214312153121631217312183121931220312213122231223312243122531226312273122831229312303123131232312333123431235312363123731238312393124031241312423124331244312453124631247312483124931250312513125231253312543125531256312573125831259312603126131262312633126431265312663126731268312693127031271312723127331274312753127631277312783127931280312813128231283312843128531286312873128831289312903129131292312933129431295312963129731298312993130031301313023130331304313053130631307313083130931310313113131231313313143131531316313173131831319313203132131322313233132431325313263132731328313293133031331313323133331334313353133631337313383133931340313413134231343313443134531346313473134831349313503135131352313533135431355313563135731358313593136031361313623136331364313653136631367313683136931370313713137231373313743137531376313773137831379313803138131382313833138431385313863138731388313893139031391313923139331394313953139631397313983139931400314013140231403314043140531406314073140831409314103141131412314133141431415314163141731418314193142031421314223142331424314253142631427314283142931430314313143231433314343143531436314373143831439314403144131442314433144431445314463144731448314493145031451314523145331454314553145631457314583145931460314613146231463314643146531466314673146831469314703147131472314733147431475314763147731478314793148031481314823148331484314853148631487314883148931490314913149231493314943149531496314973149831499315003150131502315033150431505315063150731508315093151031511315123151331514315153151631517315183151931520315213152231523315243152531526315273152831529315303153131532315333153431535315363153731538315393154031541315423154331544315453154631547315483154931550315513155231553315543155531556315573155831559315603156131562315633156431565315663156731568315693157031571315723157331574315753157631577315783157931580315813158231583315843158531586315873158831589315903159131592315933159431595315963159731598315993160031601316023160331604316053160631607316083160931610316113161231613316143161531616316173161831619316203162131622316233162431625316263162731628316293163031631316323163331634316353163631637316383163931640316413164231643316443164531646316473164831649316503165131652316533165431655316563165731658316593166031661316623166331664316653166631667316683166931670316713167231673316743167531676316773167831679316803168131682316833168431685316863168731688316893169031691316923169331694316953169631697316983169931700317013170231703317043170531706317073170831709317103171131712317133171431715317163171731718317193172031721317223172331724317253172631727317283172931730317313173231733317343173531736317373173831739317403174131742317433174431745317463174731748317493175031751317523175331754317553175631757317583175931760317613176231763317643176531766317673176831769317703177131772317733177431775317763177731778317793178031781317823178331784317853178631787317883178931790317913179231793317943179531796317973179831799318003180131802318033180431805318063180731808318093181031811318123181331814318153181631817318183181931820318213182231823318243182531826318273182831829318303183131832318333183431835318363183731838318393184031841318423184331844318453184631847318483184931850318513185231853318543185531856318573185831859318603186131862318633186431865318663186731868318693187031871318723187331874318753187631877318783187931880318813188231883318843188531886318873188831889318903189131892318933189431895318963189731898318993190031901319023190331904319053190631907319083190931910319113191231913319143191531916319173191831919319203192131922319233192431925319263192731928319293193031931319323193331934319353193631937319383193931940319413194231943319443194531946319473194831949319503195131952319533195431955319563195731958319593196031961319623196331964319653196631967319683196931970319713197231973319743197531976319773197831979319803198131982319833198431985319863198731988319893199031991319923199331994319953199631997319983199932000320013200232003320043200532006320073200832009320103201132012320133201432015320163201732018320193202032021320223202332024320253202632027320283202932030320313203232033320343203532036320373203832039320403204132042320433204432045320463204732048320493205032051320523205332054320553205632057320583205932060320613206232063320643206532066320673206832069320703207132072320733207432075320763207732078320793208032081320823208332084320853208632087320883208932090320913209232093320943209532096320973209832099321003210132102321033210432105321063210732108321093211032111321123211332114321153211632117321183211932120321213212232123321243212532126321273212832129321303213132132321333213432135321363213732138321393214032141321423214332144321453214632147321483214932150321513215232153321543215532156321573215832159321603216132162321633216432165321663216732168321693217032171321723217332174321753217632177321783217932180321813218232183321843218532186321873218832189321903219132192321933219432195321963219732198321993220032201322023220332204322053220632207322083220932210322113221232213322143221532216322173221832219322203222132222322233222432225322263222732228322293223032231322323223332234322353223632237322383223932240322413224232243322443224532246322473224832249322503225132252322533225432255322563225732258322593226032261322623226332264322653226632267322683226932270322713227232273322743227532276322773227832279322803228132282322833228432285322863228732288322893229032291322923229332294322953229632297322983229932300323013230232303323043230532306323073230832309323103231132312323133231432315323163231732318323193232032321323223232332324323253232632327323283232932330323313233232333323343233532336323373233832339323403234132342323433234432345323463234732348323493235032351323523235332354323553235632357323583235932360323613236232363323643236532366323673236832369323703237132372323733237432375323763237732378323793238032381323823238332384323853238632387323883238932390323913239232393323943239532396323973239832399324003240132402324033240432405324063240732408324093241032411324123241332414324153241632417324183241932420324213242232423324243242532426324273242832429324303243132432324333243432435324363243732438324393244032441324423244332444324453244632447324483244932450324513245232453324543245532456324573245832459324603246132462324633246432465324663246732468324693247032471324723247332474324753247632477324783247932480324813248232483324843248532486324873248832489324903249132492324933249432495324963249732498324993250032501325023250332504325053250632507325083250932510325113251232513325143251532516325173251832519325203252132522325233252432525325263252732528325293253032531325323253332534325353253632537325383253932540325413254232543325443254532546325473254832549325503255132552325533255432555325563255732558325593256032561325623256332564325653256632567325683256932570325713257232573325743257532576325773257832579325803258132582325833258432585325863258732588325893259032591325923259332594325953259632597325983259932600326013260232603326043260532606326073260832609326103261132612326133261432615326163261732618326193262032621326223262332624326253262632627326283262932630326313263232633326343263532636326373263832639326403264132642326433264432645326463264732648326493265032651326523265332654326553265632657326583265932660326613266232663326643266532666326673266832669326703267132672326733267432675326763267732678326793268032681326823268332684326853268632687326883268932690326913269232693326943269532696326973269832699327003270132702327033270432705327063270732708327093271032711327123271332714327153271632717327183271932720327213272232723327243272532726327273272832729327303273132732327333273432735327363273732738327393274032741327423274332744327453274632747327483274932750327513275232753327543275532756327573275832759327603276132762327633276432765327663276732768327693277032771327723277332774327753277632777327783277932780327813278232783327843278532786327873278832789327903279132792327933279432795327963279732798327993280032801328023280332804328053280632807328083280932810328113281232813328143281532816328173281832819328203282132822328233282432825328263282732828328293283032831328323283332834328353283632837328383283932840328413284232843328443284532846328473284832849328503285132852328533285432855328563285732858328593286032861328623286332864328653286632867328683286932870328713287232873328743287532876328773287832879328803288132882328833288432885328863288732888328893289032891328923289332894328953289632897328983289932900329013290232903329043290532906329073290832909329103291132912329133291432915329163291732918329193292032921329223292332924329253292632927329283292932930329313293232933329343293532936329373293832939329403294132942329433294432945329463294732948329493295032951329523295332954329553295632957329583295932960329613296232963329643296532966329673296832969329703297132972329733297432975329763297732978329793298032981329823298332984329853298632987329883298932990329913299232993329943299532996329973299832999330003300133002330033300433005330063300733008330093301033011330123301333014330153301633017330183301933020330213302233023330243302533026330273302833029330303303133032330333303433035330363303733038330393304033041330423304333044330453304633047330483304933050330513305233053330543305533056330573305833059330603306133062330633306433065330663306733068330693307033071330723307333074330753307633077330783307933080330813308233083330843308533086330873308833089330903309133092330933309433095330963309733098330993310033101331023310333104331053310633107331083310933110331113311233113331143311533116331173311833119331203312133122331233312433125331263312733128331293313033131331323313333134331353313633137331383313933140331413314233143331443314533146331473314833149331503315133152331533315433155331563315733158331593316033161331623316333164331653316633167331683316933170331713317233173331743317533176331773317833179331803318133182331833318433185331863318733188331893319033191331923319333194331953319633197331983319933200332013320233203332043320533206332073320833209332103321133212332133321433215332163321733218332193322033221332223322333224332253322633227332283322933230332313323233233332343323533236332373323833239332403324133242332433324433245332463324733248332493325033251332523325333254332553325633257332583325933260332613326233263332643326533266332673326833269332703327133272332733327433275332763327733278332793328033281332823328333284332853328633287332883328933290332913329233293332943329533296332973329833299333003330133302333033330433305333063330733308333093331033311333123331333314333153331633317333183331933320333213332233323333243332533326333273332833329333303333133332333333333433335333363333733338333393334033341333423334333344333453334633347333483334933350333513335233353333543335533356333573335833359333603336133362333633336433365333663336733368333693337033371333723337333374333753337633377333783337933380333813338233383333843338533386333873338833389333903339133392333933339433395333963339733398333993340033401334023340333404334053340633407334083340933410334113341233413334143341533416334173341833419334203342133422334233342433425334263342733428334293343033431334323343333434334353343633437334383343933440334413344233443334443344533446334473344833449334503345133452334533345433455334563345733458334593346033461334623346333464334653346633467334683346933470334713347233473334743347533476334773347833479334803348133482334833348433485334863348733488334893349033491334923349333494334953349633497334983349933500335013350233503335043350533506335073350833509335103351133512335133351433515335163351733518335193352033521335223352333524335253352633527335283352933530335313353233533335343353533536335373353833539335403354133542335433354433545335463354733548335493355033551335523355333554335553355633557335583355933560335613356233563335643356533566335673356833569335703357133572335733357433575335763357733578335793358033581335823358333584335853358633587335883358933590335913359233593335943359533596335973359833599336003360133602336033360433605336063360733608336093361033611336123361333614336153361633617336183361933620336213362233623336243362533626336273362833629336303363133632336333363433635336363363733638336393364033641336423364333644336453364633647336483364933650336513365233653336543365533656336573365833659336603366133662336633366433665336663366733668336693367033671336723367333674336753367633677336783367933680336813368233683336843368533686336873368833689336903369133692336933369433695336963369733698336993370033701337023370333704337053370633707337083370933710337113371233713337143371533716337173371833719337203372133722337233372433725337263372733728337293373033731337323373333734337353373633737337383373933740337413374233743337443374533746337473374833749337503375133752337533375433755337563375733758337593376033761337623376333764337653376633767337683376933770337713377233773337743377533776337773377833779337803378133782337833378433785337863378733788337893379033791337923379333794337953379633797337983379933800338013380233803338043380533806338073380833809338103381133812338133381433815338163381733818338193382033821338223382333824338253382633827338283382933830338313383233833338343383533836338373383833839338403384133842338433384433845338463384733848338493385033851338523385333854338553385633857338583385933860338613386233863338643386533866338673386833869338703387133872338733387433875338763387733878338793388033881338823388333884338853388633887338883388933890338913389233893338943389533896338973389833899339003390133902339033390433905339063390733908339093391033911339123391333914339153391633917339183391933920339213392233923339243392533926339273392833929339303393133932339333393433935339363393733938339393394033941339423394333944339453394633947339483394933950339513395233953339543395533956339573395833959339603396133962339633396433965339663396733968339693397033971339723397333974339753397633977339783397933980339813398233983339843398533986339873398833989339903399133992339933399433995339963399733998339993400034001340023400334004340053400634007340083400934010340113401234013340143401534016340173401834019340203402134022340233402434025340263402734028340293403034031340323403334034340353403634037340383403934040340413404234043340443404534046340473404834049340503405134052340533405434055340563405734058340593406034061340623406334064340653406634067340683406934070340713407234073340743407534076340773407834079340803408134082340833408434085340863408734088340893409034091340923409334094340953409634097340983409934100341013410234103341043410534106341073410834109341103411134112341133411434115341163411734118341193412034121341223412334124341253412634127341283412934130341313413234133341343413534136341373413834139341403414134142341433414434145341463414734148341493415034151341523415334154341553415634157341583415934160341613416234163341643416534166341673416834169341703417134172341733417434175341763417734178341793418034181341823418334184341853418634187341883418934190341913419234193341943419534196341973419834199342003420134202342033420434205342063420734208342093421034211342123421334214342153421634217342183421934220342213422234223342243422534226342273422834229342303423134232342333423434235342363423734238342393424034241342423424334244342453424634247342483424934250342513425234253342543425534256342573425834259342603426134262342633426434265342663426734268342693427034271342723427334274342753427634277342783427934280342813428234283342843428534286342873428834289342903429134292342933429434295342963429734298342993430034301343023430334304343053430634307343083430934310343113431234313343143431534316343173431834319343203432134322343233432434325343263432734328343293433034331343323433334334343353433634337343383433934340343413434234343343443434534346343473434834349343503435134352343533435434355343563435734358343593436034361343623436334364343653436634367343683436934370343713437234373343743437534376343773437834379343803438134382343833438434385343863438734388343893439034391343923439334394343953439634397343983439934400344013440234403344043440534406344073440834409344103441134412344133441434415344163441734418344193442034421344223442334424344253442634427344283442934430344313443234433344343443534436344373443834439344403444134442344433444434445344463444734448344493445034451344523445334454344553445634457344583445934460344613446234463344643446534466344673446834469344703447134472344733447434475344763447734478344793448034481344823448334484344853448634487344883448934490344913449234493344943449534496344973449834499345003450134502345033450434505345063450734508345093451034511345123451334514345153451634517345183451934520345213452234523345243452534526345273452834529345303453134532345333453434535345363453734538345393454034541345423454334544345453454634547345483454934550345513455234553345543455534556345573455834559345603456134562345633456434565345663456734568345693457034571345723457334574345753457634577345783457934580345813458234583345843458534586345873458834589345903459134592345933459434595345963459734598345993460034601346023460334604346053460634607346083460934610346113461234613346143461534616346173461834619346203462134622346233462434625346263462734628346293463034631346323463334634346353463634637346383463934640346413464234643346443464534646346473464834649346503465134652346533465434655346563465734658346593466034661346623466334664346653466634667346683466934670346713467234673346743467534676346773467834679346803468134682346833468434685346863468734688346893469034691346923469334694346953469634697346983469934700347013470234703347043470534706347073470834709347103471134712347133471434715347163471734718347193472034721347223472334724347253472634727347283472934730347313473234733347343473534736347373473834739347403474134742347433474434745347463474734748347493475034751347523475334754347553475634757347583475934760347613476234763347643476534766347673476834769347703477134772347733477434775347763477734778347793478034781347823478334784347853478634787347883478934790347913479234793347943479534796347973479834799348003480134802348033480434805348063480734808348093481034811348123481334814348153481634817348183481934820348213482234823348243482534826348273482834829348303483134832348333483434835348363483734838348393484034841348423484334844348453484634847348483484934850348513485234853348543485534856348573485834859348603486134862348633486434865348663486734868348693487034871348723487334874348753487634877348783487934880348813488234883348843488534886348873488834889348903489134892348933489434895348963489734898348993490034901349023490334904349053490634907349083490934910349113491234913349143491534916349173491834919349203492134922349233492434925349263492734928349293493034931349323493334934349353493634937349383493934940349413494234943349443494534946349473494834949349503495134952349533495434955349563495734958349593496034961349623496334964349653496634967349683496934970349713497234973349743497534976349773497834979349803498134982349833498434985349863498734988349893499034991349923499334994349953499634997349983499935000350013500235003350043500535006350073500835009350103501135012350133501435015350163501735018350193502035021350223502335024350253502635027350283502935030350313503235033350343503535036350373503835039350403504135042350433504435045350463504735048350493505035051350523505335054350553505635057350583505935060350613506235063350643506535066350673506835069350703507135072350733507435075350763507735078350793508035081350823508335084350853508635087350883508935090350913509235093350943509535096350973509835099351003510135102351033510435105351063510735108351093511035111351123511335114351153511635117351183511935120351213512235123351243512535126351273512835129351303513135132351333513435135351363513735138351393514035141351423514335144351453514635147351483514935150351513515235153351543515535156351573515835159351603516135162351633516435165351663516735168351693517035171351723517335174351753517635177351783517935180351813518235183351843518535186351873518835189351903519135192351933519435195351963519735198351993520035201352023520335204352053520635207352083520935210352113521235213352143521535216352173521835219352203522135222352233522435225352263522735228352293523035231352323523335234352353523635237352383523935240352413524235243352443524535246352473524835249352503525135252352533525435255352563525735258352593526035261352623526335264352653526635267352683526935270352713527235273352743527535276352773527835279352803528135282352833528435285352863528735288352893529035291352923529335294352953529635297352983529935300353013530235303353043530535306353073530835309353103531135312353133531435315353163531735318353193532035321353223532335324353253532635327353283532935330353313533235333353343533535336353373533835339353403534135342353433534435345353463534735348353493535035351353523535335354353553535635357353583535935360353613536235363353643536535366353673536835369353703537135372353733537435375353763537735378353793538035381353823538335384353853538635387353883538935390353913539235393353943539535396353973539835399354003540135402354033540435405354063540735408354093541035411354123541335414354153541635417354183541935420354213542235423354243542535426354273542835429354303543135432354333543435435354363543735438354393544035441354423544335444354453544635447354483544935450354513545235453354543545535456354573545835459354603546135462354633546435465354663546735468354693547035471354723547335474354753547635477354783547935480354813548235483354843548535486354873548835489354903549135492354933549435495354963549735498354993550035501355023550335504355053550635507355083550935510355113551235513355143551535516355173551835519355203552135522355233552435525355263552735528355293553035531355323553335534355353553635537355383553935540355413554235543355443554535546355473554835549355503555135552355533555435555355563555735558355593556035561355623556335564355653556635567355683556935570355713557235573355743557535576355773557835579355803558135582355833558435585355863558735588355893559035591355923559335594355953559635597355983559935600356013560235603356043560535606356073560835609356103561135612356133561435615356163561735618356193562035621356223562335624356253562635627356283562935630356313563235633356343563535636356373563835639356403564135642356433564435645356463564735648356493565035651356523565335654356553565635657356583565935660356613566235663356643566535666356673566835669356703567135672356733567435675356763567735678356793568035681356823568335684356853568635687356883568935690356913569235693356943569535696356973569835699357003570135702357033570435705357063570735708357093571035711357123571335714357153571635717357183571935720357213572235723357243572535726357273572835729357303573135732357333573435735357363573735738357393574035741357423574335744357453574635747357483574935750357513575235753357543575535756357573575835759357603576135762357633576435765357663576735768357693577035771357723577335774357753577635777357783577935780357813578235783357843578535786357873578835789357903579135792357933579435795357963579735798357993580035801358023580335804358053580635807358083580935810358113581235813358143581535816358173581835819358203582135822358233582435825358263582735828358293583035831358323583335834358353583635837358383583935840358413584235843358443584535846358473584835849358503585135852358533585435855358563585735858358593586035861358623586335864358653586635867358683586935870358713587235873358743587535876358773587835879358803588135882358833588435885358863588735888358893589035891358923589335894358953589635897358983589935900359013590235903359043590535906359073590835909359103591135912359133591435915359163591735918359193592035921359223592335924359253592635927359283592935930359313593235933359343593535936359373593835939359403594135942359433594435945359463594735948359493595035951359523595335954359553595635957359583595935960359613596235963359643596535966359673596835969359703597135972359733597435975359763597735978359793598035981359823598335984359853598635987359883598935990359913599235993359943599535996359973599835999360003600136002360033600436005360063600736008360093601036011360123601336014360153601636017360183601936020360213602236023360243602536026360273602836029360303603136032360333603436035360363603736038360393604036041360423604336044360453604636047360483604936050360513605236053360543605536056360573605836059360603606136062360633606436065360663606736068360693607036071360723607336074360753607636077360783607936080360813608236083360843608536086360873608836089360903609136092360933609436095360963609736098360993610036101361023610336104361053610636107361083610936110361113611236113361143611536116361173611836119361203612136122361233612436125361263612736128361293613036131361323613336134361353613636137361383613936140361413614236143361443614536146361473614836149361503615136152361533615436155361563615736158361593616036161361623616336164361653616636167361683616936170361713617236173361743617536176361773617836179361803618136182361833618436185361863618736188361893619036191361923619336194361953619636197361983619936200362013620236203362043620536206362073620836209362103621136212362133621436215362163621736218362193622036221362223622336224362253622636227362283622936230362313623236233362343623536236362373623836239362403624136242362433624436245362463624736248362493625036251362523625336254362553625636257362583625936260362613626236263362643626536266362673626836269362703627136272362733627436275362763627736278362793628036281362823628336284362853628636287362883628936290362913629236293362943629536296362973629836299363003630136302363033630436305363063630736308363093631036311363123631336314363153631636317363183631936320363213632236323363243632536326363273632836329363303633136332363333633436335363363633736338363393634036341363423634336344363453634636347363483634936350363513635236353363543635536356363573635836359363603636136362363633636436365363663636736368363693637036371363723637336374363753637636377363783637936380363813638236383363843638536386363873638836389363903639136392363933639436395363963639736398363993640036401364023640336404364053640636407364083640936410364113641236413364143641536416364173641836419364203642136422364233642436425364263642736428364293643036431364323643336434364353643636437364383643936440364413644236443364443644536446364473644836449364503645136452364533645436455364563645736458364593646036461364623646336464364653646636467364683646936470364713647236473364743647536476364773647836479364803648136482364833648436485364863648736488364893649036491364923649336494364953649636497364983649936500365013650236503365043650536506365073650836509365103651136512365133651436515365163651736518365193652036521365223652336524365253652636527365283652936530365313653236533365343653536536365373653836539365403654136542365433654436545365463654736548365493655036551365523655336554365553655636557365583655936560365613656236563365643656536566365673656836569365703657136572365733657436575365763657736578365793658036581365823658336584365853658636587365883658936590365913659236593365943659536596365973659836599366003660136602366033660436605366063660736608366093661036611366123661336614366153661636617366183661936620366213662236623366243662536626366273662836629366303663136632366333663436635366363663736638366393664036641366423664336644366453664636647366483664936650366513665236653366543665536656366573665836659366603666136662366633666436665366663666736668366693667036671366723667336674366753667636677366783667936680366813668236683366843668536686366873668836689366903669136692366933669436695366963669736698366993670036701367023670336704367053670636707367083670936710367113671236713367143671536716367173671836719367203672136722367233672436725367263672736728367293673036731367323673336734367353673636737367383673936740367413674236743367443674536746367473674836749367503675136752367533675436755367563675736758367593676036761367623676336764367653676636767367683676936770367713677236773367743677536776367773677836779367803678136782367833678436785367863678736788367893679036791367923679336794367953679636797367983679936800368013680236803368043680536806368073680836809368103681136812368133681436815368163681736818368193682036821368223682336824368253682636827368283682936830368313683236833368343683536836368373683836839368403684136842368433684436845368463684736848368493685036851368523685336854368553685636857368583685936860368613686236863368643686536866368673686836869368703687136872368733687436875368763687736878368793688036881368823688336884368853688636887368883688936890368913689236893368943689536896368973689836899369003690136902369033690436905369063690736908369093691036911369123691336914369153691636917369183691936920369213692236923369243692536926369273692836929369303693136932369333693436935369363693736938369393694036941369423694336944369453694636947369483694936950369513695236953369543695536956369573695836959369603696136962369633696436965369663696736968369693697036971369723697336974369753697636977369783697936980369813698236983369843698536986369873698836989369903699136992369933699436995369963699736998369993700037001370023700337004370053700637007370083700937010370113701237013370143701537016370173701837019370203702137022370233702437025370263702737028370293703037031370323703337034370353703637037370383703937040370413704237043370443704537046370473704837049370503705137052370533705437055370563705737058370593706037061370623706337064370653706637067370683706937070370713707237073370743707537076370773707837079370803708137082370833708437085370863708737088370893709037091370923709337094370953709637097370983709937100371013710237103371043710537106371073710837109371103711137112371133711437115371163711737118371193712037121371223712337124371253712637127371283712937130371313713237133371343713537136371373713837139371403714137142371433714437145371463714737148371493715037151371523715337154371553715637157371583715937160371613716237163371643716537166371673716837169371703717137172371733717437175371763717737178371793718037181371823718337184371853718637187371883718937190371913719237193371943719537196371973719837199372003720137202372033720437205372063720737208372093721037211372123721337214372153721637217372183721937220372213722237223372243722537226372273722837229372303723137232372333723437235372363723737238372393724037241372423724337244372453724637247372483724937250372513725237253372543725537256372573725837259372603726137262372633726437265372663726737268372693727037271372723727337274372753727637277372783727937280372813728237283372843728537286372873728837289372903729137292372933729437295372963729737298372993730037301373023730337304373053730637307373083730937310373113731237313373143731537316373173731837319373203732137322373233732437325373263732737328373293733037331373323733337334373353733637337373383733937340373413734237343373443734537346373473734837349373503735137352373533735437355373563735737358373593736037361373623736337364373653736637367373683736937370373713737237373373743737537376373773737837379373803738137382373833738437385373863738737388373893739037391373923739337394373953739637397373983739937400374013740237403374043740537406374073740837409374103741137412374133741437415374163741737418374193742037421374223742337424374253742637427374283742937430374313743237433374343743537436374373743837439374403744137442374433744437445374463744737448374493745037451374523745337454374553745637457374583745937460374613746237463374643746537466374673746837469374703747137472374733747437475374763747737478374793748037481374823748337484374853748637487374883748937490374913749237493374943749537496374973749837499375003750137502375033750437505375063750737508375093751037511375123751337514375153751637517375183751937520375213752237523375243752537526375273752837529375303753137532375333753437535375363753737538375393754037541375423754337544375453754637547375483754937550375513755237553375543755537556375573755837559375603756137562375633756437565375663756737568375693757037571375723757337574375753757637577375783757937580375813758237583375843758537586375873758837589375903759137592375933759437595375963759737598375993760037601376023760337604376053760637607376083760937610376113761237613376143761537616376173761837619376203762137622376233762437625376263762737628376293763037631376323763337634376353763637637376383763937640376413764237643376443764537646376473764837649376503765137652376533765437655376563765737658376593766037661376623766337664376653766637667376683766937670376713767237673376743767537676376773767837679376803768137682376833768437685376863768737688376893769037691376923769337694376953769637697376983769937700377013770237703377043770537706377073770837709377103771137712377133771437715377163771737718377193772037721377223772337724377253772637727377283772937730377313773237733377343773537736377373773837739377403774137742377433774437745377463774737748377493775037751377523775337754377553775637757377583775937760377613776237763377643776537766377673776837769377703777137772377733777437775377763777737778377793778037781377823778337784377853778637787377883778937790377913779237793377943779537796377973779837799378003780137802378033780437805378063780737808378093781037811378123781337814378153781637817378183781937820378213782237823378243782537826378273782837829378303783137832378333783437835378363783737838378393784037841378423784337844378453784637847378483784937850378513785237853378543785537856378573785837859378603786137862378633786437865378663786737868378693787037871378723787337874378753787637877378783787937880378813788237883378843788537886378873788837889378903789137892378933789437895378963789737898378993790037901379023790337904379053790637907379083790937910379113791237913379143791537916379173791837919379203792137922379233792437925379263792737928379293793037931379323793337934379353793637937379383793937940379413794237943379443794537946379473794837949379503795137952379533795437955379563795737958379593796037961379623796337964379653796637967379683796937970379713797237973379743797537976379773797837979379803798137982379833798437985379863798737988379893799037991379923799337994379953799637997379983799938000380013800238003380043800538006380073800838009380103801138012380133801438015380163801738018380193802038021380223802338024380253802638027380283802938030380313803238033380343803538036380373803838039380403804138042380433804438045380463804738048380493805038051380523805338054380553805638057380583805938060380613806238063380643806538066380673806838069380703807138072380733807438075380763807738078380793808038081380823808338084380853808638087380883808938090380913809238093380943809538096380973809838099381003810138102381033810438105381063810738108381093811038111381123811338114381153811638117381183811938120381213812238123381243812538126381273812838129381303813138132381333813438135381363813738138381393814038141381423814338144381453814638147381483814938150381513815238153381543815538156381573815838159381603816138162381633816438165381663816738168381693817038171381723817338174381753817638177381783817938180381813818238183381843818538186381873818838189381903819138192381933819438195381963819738198381993820038201382023820338204382053820638207382083820938210382113821238213382143821538216382173821838219382203822138222382233822438225382263822738228382293823038231382323823338234382353823638237382383823938240382413824238243382443824538246382473824838249382503825138252382533825438255382563825738258382593826038261382623826338264382653826638267382683826938270382713827238273382743827538276382773827838279382803828138282382833828438285382863828738288382893829038291382923829338294382953829638297382983829938300383013830238303383043830538306383073830838309383103831138312383133831438315383163831738318383193832038321383223832338324383253832638327383283832938330383313833238333383343833538336383373833838339383403834138342383433834438345383463834738348383493835038351383523835338354383553835638357383583835938360383613836238363383643836538366383673836838369383703837138372383733837438375383763837738378383793838038381383823838338384383853838638387383883838938390383913839238393383943839538396383973839838399384003840138402384033840438405384063840738408384093841038411384123841338414384153841638417384183841938420384213842238423384243842538426384273842838429384303843138432384333843438435384363843738438384393844038441384423844338444384453844638447384483844938450384513845238453384543845538456384573845838459384603846138462384633846438465384663846738468384693847038471384723847338474384753847638477384783847938480384813848238483384843848538486384873848838489384903849138492384933849438495384963849738498384993850038501385023850338504385053850638507385083850938510385113851238513385143851538516385173851838519385203852138522385233852438525385263852738528385293853038531385323853338534385353853638537385383853938540385413854238543385443854538546385473854838549385503855138552385533855438555385563855738558385593856038561385623856338564385653856638567385683856938570385713857238573385743857538576385773857838579385803858138582385833858438585385863858738588385893859038591385923859338594385953859638597385983859938600386013860238603386043860538606386073860838609386103861138612386133861438615386163861738618386193862038621386223862338624386253862638627386283862938630386313863238633386343863538636386373863838639386403864138642386433864438645386463864738648386493865038651386523865338654386553865638657386583865938660386613866238663386643866538666386673866838669386703867138672386733867438675386763867738678386793868038681386823868338684386853868638687386883868938690386913869238693386943869538696386973869838699387003870138702387033870438705387063870738708387093871038711387123871338714387153871638717387183871938720387213872238723387243872538726387273872838729387303873138732387333873438735387363873738738387393874038741387423874338744387453874638747387483874938750387513875238753387543875538756387573875838759387603876138762387633876438765387663876738768387693877038771387723877338774387753877638777387783877938780387813878238783387843878538786387873878838789387903879138792387933879438795387963879738798387993880038801388023880338804388053880638807388083880938810388113881238813388143881538816388173881838819388203882138822388233882438825388263882738828388293883038831388323883338834388353883638837388383883938840388413884238843388443884538846388473884838849388503885138852388533885438855388563885738858388593886038861388623886338864388653886638867388683886938870388713887238873388743887538876388773887838879388803888138882388833888438885388863888738888388893889038891388923889338894388953889638897388983889938900389013890238903389043890538906389073890838909389103891138912389133891438915389163891738918389193892038921389223892338924389253892638927389283892938930389313893238933389343893538936389373893838939389403894138942389433894438945389463894738948389493895038951389523895338954389553895638957389583895938960389613896238963389643896538966389673896838969389703897138972389733897438975389763897738978389793898038981389823898338984389853898638987389883898938990389913899238993389943899538996389973899838999390003900139002390033900439005390063900739008390093901039011390123901339014390153901639017390183901939020390213902239023390243902539026390273902839029390303903139032390333903439035390363903739038390393904039041390423904339044390453904639047390483904939050390513905239053390543905539056390573905839059390603906139062390633906439065390663906739068390693907039071390723907339074390753907639077390783907939080390813908239083390843908539086390873908839089390903909139092390933909439095390963909739098390993910039101391023910339104391053910639107391083910939110391113911239113391143911539116391173911839119391203912139122391233912439125391263912739128391293913039131391323913339134391353913639137391383913939140391413914239143391443914539146391473914839149391503915139152391533915439155391563915739158391593916039161391623916339164391653916639167391683916939170391713917239173391743917539176391773917839179391803918139182391833918439185391863918739188391893919039191391923919339194391953919639197391983919939200392013920239203392043920539206392073920839209392103921139212392133921439215392163921739218392193922039221392223922339224392253922639227392283922939230392313923239233392343923539236392373923839239392403924139242392433924439245392463924739248392493925039251392523925339254392553925639257392583925939260392613926239263392643926539266392673926839269392703927139272392733927439275392763927739278392793928039281392823928339284392853928639287392883928939290392913929239293392943929539296392973929839299393003930139302393033930439305393063930739308393093931039311393123931339314393153931639317393183931939320393213932239323393243932539326393273932839329393303933139332393333933439335393363933739338393393934039341393423934339344393453934639347393483934939350393513935239353393543935539356393573935839359393603936139362393633936439365393663936739368393693937039371393723937339374393753937639377393783937939380393813938239383393843938539386393873938839389393903939139392393933939439395393963939739398393993940039401394023940339404394053940639407394083940939410394113941239413394143941539416394173941839419394203942139422394233942439425394263942739428394293943039431394323943339434394353943639437394383943939440394413944239443394443944539446394473944839449394503945139452394533945439455394563945739458394593946039461394623946339464394653946639467394683946939470394713947239473394743947539476394773947839479394803948139482394833948439485394863948739488394893949039491394923949339494 |
- /* RetroArch - A frontend for libretro.
- * Copyright (C) 2010-2014 - Hans-Kristian Arntzen
- * Copyright (C) 2011-2017 - Daniel De Matteis
- * Copyright (C) 2012-2015 - Michael Lelli
- * Copyright (C) 2014-2017 - Jean-André Santoni
- * Copyright (C) 2016-2019 - Brad Parker
- * Copyright (C) 2016-2019 - Andrés Suárez (input mapper/Discord code)
- * Copyright (C) 2016-2017 - Gregor Richards (network code)
- *
- * RetroArch is free software: you can redistribute it and/or modify it under the terms
- * of the GNU General Public License as published by the Free Software Found-
- * ation, either version 3 of the License, or (at your option) any later version.
- *
- * RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
- * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
- * PURPOSE. See the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along with RetroArch.
- * If not, see <http://www.gnu.org/licenses/>.
- */
- #ifdef _WIN32
- #ifdef _XBOX
- #include <xtl.h>
- #else
- #define WIN32_LEAN_AND_MEAN
- #include <windows.h>
- #endif
- #if defined(DEBUG) && defined(HAVE_DRMINGW)
- #include "exchndl.h"
- #endif
- #endif
- #if defined(DINGUX)
- #include <sys/types.h>
- #include <unistd.h>
- #endif
- #if (defined(__linux__) || defined(__unix__) || defined(DINGUX)) && !defined(EMSCRIPTEN)
- #include <signal.h>
- #endif
- #if defined(_WIN32_WINNT) && _WIN32_WINNT < 0x0500 || defined(_XBOX)
- #ifndef LEGACY_WIN32
- #define LEGACY_WIN32
- #endif
- #endif
- #if defined(_WIN32) && !defined(_XBOX) && !defined(__WINRT__)
- #include <objbase.h>
- #include <process.h>
- #endif
- #include <stdio.h>
- #include <stdlib.h>
- #include <stdarg.h>
- #include <stdint.h>
- #include <string.h>
- #include <ctype.h>
- #include <errno.h>
- #include <setjmp.h>
- #include <math.h>
- #include <locale.h>
- #include <boolean.h>
- #include <clamping.h>
- #include <string/stdstring.h>
- #include <streams/stdin_stream.h>
- #include <dynamic/dylib.h>
- #include <file/config_file.h>
- #include <lists/string_list.h>
- #include <memalign.h>
- #include <retro_math.h>
- #include <retro_timers.h>
- #include <encodings/utf.h>
- #include <time/rtime.h>
- #include <gfx/scaler/pixconv.h>
- #include <gfx/scaler/scaler.h>
- #include <gfx/video_frame.h>
- #include <libretro.h>
- #define VFS_FRONTEND
- #include <vfs/vfs_implementation.h>
- #include <features/features_cpu.h>
- #include <compat/strl.h>
- #include <compat/strcasestr.h>
- #include <compat/getopt.h>
- #include <audio/conversion/float_to_s16.h>
- #include <audio/conversion/s16_to_float.h>
- #ifdef HAVE_AUDIOMIXER
- #include <audio/audio_mixer.h>
- #endif
- #ifdef HAVE_DSP_FILTER
- #include <audio/dsp_filter.h>
- #endif
- #include <compat/posix_string.h>
- #include <streams/file_stream.h>
- #include <streams/interface_stream.h>
- #include <file/file_path.h>
- #include <retro_assert.h>
- #include <retro_miscellaneous.h>
- #include <queues/message_queue.h>
- #include <queues/task_queue.h>
- #include <lists/dir_list.h>
- #ifdef HAVE_NETWORKING
- #include <net/net_http.h>
- #endif
- #ifdef WIIU
- #include <wiiu/os/energy.h>
- #endif
- #ifdef EMSCRIPTEN
- #include <emscripten/emscripten.h>
- #endif
- #ifdef HAVE_LIBNX
- #include <switch.h>
- #include "switch_performance_profiles.h"
- #endif
- #if defined(ANDROID)
- #include "play_feature_delivery/play_feature_delivery.h"
- #endif
- #ifdef HAVE_DISCORD
- #include <discord_rpc.h>
- #include "deps/discord-rpc/include/discord_rpc.h"
- #include "network/discord.h"
- #endif
- #include "config.def.h"
- #include "config.def.keybinds.h"
- #include "runtime_file.h"
- #ifdef HAVE_CONFIG_H
- #include "config.h"
- #endif
- #ifdef HAVE_NETWORKING
- #include <net/net_compat.h>
- #include <net/net_socket.h>
- #endif
- #include <audio/audio_resampler.h>
- #include "gfx/gfx_animation.h"
- #include "gfx/gfx_display.h"
- #include "gfx/gfx_thumbnail.h"
- #include "gfx/video_filter.h"
- #include "input/input_osk.h"
- #ifdef HAVE_MENU
- #include "menu/menu_cbs.h"
- #include "menu/menu_driver.h"
- #include "menu/menu_input.h"
- #include "menu/menu_dialog.h"
- #include "menu/menu_input_bind_dialog.h"
- #endif
- #if defined(HAVE_CG) || defined(HAVE_GLSL) || defined(HAVE_SLANG) || defined(HAVE_HLSL)
- #include "menu/menu_shader.h"
- #endif
- #ifdef HAVE_GFX_WIDGETS
- #include "gfx/gfx_widgets.h"
- #endif
- #include "input/input_keymaps.h"
- #include "input/input_remapping.h"
- #ifdef HAVE_CHEEVOS
- #include "cheevos/cheevos.h"
- #endif
- #ifdef HAVE_TRANSLATE
- #include <encodings/base64.h>
- #include <formats/rbmp.h>
- #include <formats/rpng.h>
- #include <formats/rjson.h>
- #include "translation_defines.h"
- #endif
- #ifdef HAVE_DISCORD
- #include "network/discord.h"
- #endif
- #ifdef HAVE_NETWORKING
- #include "network/netplay/netplay.h"
- #include "network/netplay/netplay_private.h"
- #include "network/netplay/netplay_discovery.h"
- #endif
- #ifdef HAVE_THREADS
- #include <rthreads/rthreads.h>
- #endif
- #if defined(HAVE_OPENGL)
- #include "gfx/common/gl_common.h"
- #elif defined(HAVE_OPENGL_CORE)
- #include "gfx/common/gl_core_common.h"
- #endif
- #include "autosave.h"
- #include "command.h"
- #include "config.features.h"
- #include "cores/internal_cores.h"
- #include "content.h"
- #include "core_type.h"
- #include "core_info.h"
- #include "dynamic.h"
- #include "defaults.h"
- #include "driver.h"
- #include "msg_hash.h"
- #include "paths.h"
- #include "file_path_special.h"
- #include "ui/ui_companion_driver.h"
- #include "verbosity.h"
- #include "frontend/frontend_driver.h"
- #ifdef HAVE_THREADS
- #include "gfx/video_thread_wrapper.h"
- #endif
- #include "gfx/video_display_server.h"
- #include "gfx/video_crt_switch.h"
- #include "bluetooth/bluetooth_driver.h"
- #include "wifi/wifi_driver.h"
- #include "led/led_driver.h"
- #include "midi/midi_driver.h"
- #include "core.h"
- #include "configuration.h"
- #include "list_special.h"
- #include "core_option_manager.h"
- #ifdef HAVE_CHEATS
- #include "cheat_manager.h"
- #endif
- #ifdef HAVE_REWIND
- #include "state_manager.h"
- #endif
- #ifdef HAVE_AUDIOMIXER
- #include "tasks/task_audio_mixer.h"
- #endif
- #include "tasks/task_content.h"
- #include "tasks/task_file_transfer.h"
- #include "tasks/task_powerstate.h"
- #include "tasks/tasks_internal.h"
- #include "performance_counters.h"
- #include "version.h"
- #include "version_git.h"
- #include "retroarch.h"
- #ifdef HAVE_ACCESSIBILITY
- #include "accessibility.h"
- #endif
- #ifdef HAVE_THREADS
- #include "audio/audio_thread_wrapper.h"
- #endif
- #ifdef HAVE_LANGEXTRA
- /* This file has a UTF8 BOM, we assume HAVE_LANGEXTRA
- * is only enabled for compilers that can support this. */
- #include "input/input_osk_utf8_pages.h"
- #endif
- /* RetroArch global state / macros */
- #include "retroarch_data.h"
- /* Forward declarations */
- #include "retroarch_fwd_decls.h"
- /* GLOBAL POINTER GETTERS */
- #ifdef HAVE_NETWORKING
- struct netplay_room* netplay_get_host_room(void)
- {
- struct rarch_state *p_rarch = &rarch_st;
- return &p_rarch->netplay_host_room;
- }
- #endif
- #ifdef HAVE_REWIND
- bool state_manager_frame_is_reversed(void)
- {
- struct rarch_state *p_rarch = &rarch_st;
- struct state_manager_rewind_state
- *rewind_st = &p_rarch->rewind_st;
- return rewind_st->frame_is_reversed;
- }
- #endif
- gfx_animation_t *anim_get_ptr(void)
- {
- struct rarch_state *p_rarch = &rarch_st;
- return &p_rarch->anim;
- }
- content_state_t *content_state_get_ptr(void)
- {
- struct rarch_state *p_rarch = &rarch_st;
- return &p_rarch->content_st;
- }
- /* Get the current subsystem rom id */
- unsigned content_get_subsystem_rom_id(void)
- {
- struct rarch_state *p_rarch = &rarch_st;
- content_state_t *p_content = &p_rarch->content_st;
- return p_content->pending_subsystem_rom_id;
- }
- /* Get the current subsystem */
- int content_get_subsystem(void)
- {
- struct rarch_state *p_rarch = &rarch_st;
- content_state_t *p_content = &p_rarch->content_st;
- return p_content->pending_subsystem_id;
- }
- int input_event_get_osk_ptr(void)
- {
- struct rarch_state *p_rarch = &rarch_st;
- return p_rarch->osk_ptr;
- }
- char **input_event_get_osk_grid(void)
- {
- struct rarch_state *p_rarch = &rarch_st;
- return p_rarch->osk_grid;
- }
- core_info_state_t *coreinfo_get_ptr(void)
- {
- struct rarch_state *p_rarch = &rarch_st;
- return &p_rarch->core_info_st;
- }
- gfx_thumbnail_state_t *gfx_thumb_get_ptr(void)
- {
- struct rarch_state *p_rarch = &rarch_st;
- return &p_rarch->gfx_thumb_state;
- }
- #ifdef HAVE_MENU
- menu_handle_t *menu_driver_get_ptr(void)
- {
- struct rarch_state *p_rarch = &rarch_st;
- return p_rarch->menu_driver_data;
- }
- size_t menu_navigation_get_selection(void)
- {
- struct rarch_state *p_rarch = &rarch_st;
- struct menu_state *menu_st = &p_rarch->menu_driver_state;
- return menu_st->selection_ptr;
- }
- #endif
- struct retro_hw_render_callback *video_driver_get_hw_context(void)
- {
- struct rarch_state *p_rarch = &rarch_st;
- return VIDEO_DRIVER_GET_HW_CONTEXT_INTERNAL(p_rarch);
- }
- struct retro_system_av_info *video_viewport_get_system_av_info(void)
- {
- struct rarch_state *p_rarch = &rarch_st;
- return &p_rarch->video_driver_av_info;
- }
- gfx_display_t *disp_get_ptr(void)
- {
- struct rarch_state *p_rarch = &rarch_st;
- return &p_rarch->dispgfx;
- }
- #ifdef HAVE_GFX_WIDGETS
- void *dispwidget_get_ptr(void)
- {
- struct rarch_state *p_rarch = &rarch_st;
- return &p_rarch->dispwidget_st;
- }
- #endif
- settings_t *config_get_ptr(void)
- {
- struct rarch_state *p_rarch = &rarch_st;
- return p_rarch->configuration_settings;
- }
- global_t *global_get_ptr(void)
- {
- struct rarch_state *p_rarch = &rarch_st;
- return &p_rarch->g_extern;
- }
- #ifdef HAVE_THREADS
- /**
- * video_thread_get_ptr:
- * @drv : Found driver.
- *
- * Gets the underlying video driver associated with the
- * threaded video wrapper. Sets @drv to the found
- * video driver.
- *
- * Returns: Video driver data of the video driver associated
- * with the threaded wrapper (if successful). If not successful,
- * NULL.
- **/
- static void *video_thread_get_ptr(struct rarch_state *p_rarch)
- {
- void *data = VIDEO_DRIVER_GET_PTR_INTERNAL(p_rarch, true);
- const thread_video_t *thr = (const thread_video_t*)data;
- if (thr)
- return thr->driver_data;
- return NULL;
- }
- #endif
- /**
- * video_driver_get_ptr:
- *
- * Use this if you need the real video driver
- * and driver data pointers.
- *
- * Returns: video driver's userdata.
- **/
- void *video_driver_get_ptr(bool force_nonthreaded_data)
- {
- struct rarch_state *p_rarch = &rarch_st;
- return VIDEO_DRIVER_GET_PTR_INTERNAL(p_rarch, force_nonthreaded_data);
- }
- static int16_t input_state_wrap(
- input_driver_t *current_input,
- void *data,
- const input_device_driver_t *joypad,
- const input_device_driver_t *sec_joypad,
- rarch_joypad_info_t *joypad_info,
- const struct retro_keybind **binds,
- bool keyboard_mapping_blocked,
- unsigned port,
- unsigned device,
- unsigned idx,
- unsigned id)
- {
- int16_t ret = 0;
- /* Do a bitwise OR to combine input states together */
- if (device == RETRO_DEVICE_JOYPAD)
- {
- if (id == RETRO_DEVICE_ID_JOYPAD_MASK)
- {
- ret |= joypad->state(
- joypad_info, binds[port], port);
- #ifdef HAVE_MFI
- if (sec_joypad)
- ret |= sec_joypad->state(
- joypad_info, binds[port], port);
- #endif
- }
- else
- {
- /* Do a bitwise OR to combine both input
- * states together */
- if (binds[port][id].valid)
- {
- if (button_is_pressed(
- joypad,
- joypad_info, binds[port], port, id))
- return 1;
- #ifdef HAVE_MFI
- else if (sec_joypad &&
- button_is_pressed(
- sec_joypad,
- joypad_info, binds[port], port, id))
- return 1;
- #endif
- }
- }
- }
- if (current_input->input_state)
- ret |= current_input->input_state(
- data,
- joypad,
- sec_joypad,
- joypad_info,
- binds,
- keyboard_mapping_blocked,
- port,
- device,
- idx,
- id);
- return ret;
- }
- /* DRIVERS */
- /**
- * driver_find_index:
- * @label : string of driver type to be found.
- * @drv : identifier of driver to be found.
- *
- * Find index of the driver, based on @label.
- *
- * Returns: -1 if no driver based on @label and @drv found, otherwise
- * index number of the driver found in the array.
- **/
- static int driver_find_index(const char *label, const char *drv)
- {
- unsigned i;
- char str[256];
- str[0] = '\0';
- for (i = 0;
- find_driver_nonempty(label, i, str, sizeof(str)) != NULL; i++)
- {
- if (string_is_empty(str))
- break;
- if (string_is_equal_noncase(drv, str))
- return i;
- }
- return -1;
- }
- /**
- * driver_find_last:
- * @label : string of driver type to be found.
- * @s : identifier of driver to be found.
- * @len : size of @s.
- *
- * Find last driver in driver array.
- **/
- static void driver_find_last(const char *label, char *s, size_t len)
- {
- unsigned i;
- for (i = 0;
- find_driver_nonempty(label, i, s, len) != NULL; i++) { }
- if (i)
- i = i - 1;
- else
- i = 0;
- find_driver_nonempty(label, i, s, len);
- }
- /**
- * driver_find_prev:
- * @label : string of driver type to be found.
- * @s : identifier of driver to be found.
- * @len : size of @s.
- *
- * Find previous driver in driver array.
- **/
- static bool driver_find_prev(const char *label, char *s, size_t len)
- {
- int i = driver_find_index(label, s);
- if (i > 0)
- {
- find_driver_nonempty(label, i - 1, s, len);
- return true;
- }
- RARCH_WARN(
- "Couldn't find any previous driver (current one: \"%s\").\n", s);
- return false;
- }
- /**
- * driver_find_next:
- * @label : string of driver type to be found.
- * @s : identifier of driver to be found.
- * @len : size of @s.
- *
- * Find next driver in driver array.
- **/
- static bool driver_find_next(const char *label, char *s, size_t len)
- {
- int i = driver_find_index(label, s);
- if (i >= 0 && string_is_not_equal(s, "null"))
- {
- find_driver_nonempty(label, i + 1, s, len);
- return true;
- }
- RARCH_WARN("%s (current one: \"%s\").\n",
- msg_hash_to_str(MSG_COULD_NOT_FIND_ANY_NEXT_DRIVER),
- s);
- return false;
- }
- #ifdef HAVE_MENU
- static int menu_dialog_iterate(
- menu_dialog_t *p_dialog,
- char *s, size_t len,
- retro_time_t current_time)
- {
- switch (p_dialog->current_type)
- {
- case MENU_DIALOG_WELCOME:
- {
- static rarch_timer_t timer;
- if (!timer.timer_begin)
- {
- RARCH_TIMER_BEGIN_NEW_TIME_USEC(timer,
- cpu_features_get_time_usec(),
- 3 * 1000000);
- timer.timer_begin = true;
- timer.timer_end = false;
- }
- RARCH_TIMER_TICK(timer, current_time);
- msg_hash_get_help_enum(
- MENU_ENUM_LABEL_WELCOME_TO_RETROARCH,
- s, len);
- if (!timer.timer_end && RARCH_TIMER_HAS_EXPIRED(timer))
- {
- RARCH_TIMER_END(timer);
- p_dialog->current_type = MENU_DIALOG_NONE;
- return 1;
- }
- }
- break;
- case MENU_DIALOG_HELP_CONTROLS:
- {
- unsigned i;
- char s2[PATH_MAX_LENGTH];
- const unsigned binds[] = {
- RETRO_DEVICE_ID_JOYPAD_UP,
- RETRO_DEVICE_ID_JOYPAD_DOWN,
- RETRO_DEVICE_ID_JOYPAD_A,
- RETRO_DEVICE_ID_JOYPAD_B,
- RETRO_DEVICE_ID_JOYPAD_SELECT,
- RETRO_DEVICE_ID_JOYPAD_START,
- RARCH_MENU_TOGGLE,
- RARCH_QUIT_KEY,
- RETRO_DEVICE_ID_JOYPAD_X,
- RETRO_DEVICE_ID_JOYPAD_Y,
- };
- char desc[ARRAY_SIZE(binds)][64];
- for (i = 0; i < ARRAY_SIZE(binds); i++)
- desc[i][0] = '\0';
- for (i = 0; i < ARRAY_SIZE(binds); i++)
- {
- const struct retro_keybind *keybind = &input_config_binds[0][binds[i]];
- const struct retro_keybind *auto_bind =
- (const struct retro_keybind*)
- input_config_get_bind_auto(0, binds[i]);
- input_config_get_bind_string(desc[i],
- keybind, auto_bind, sizeof(desc[i]));
- }
- s2[0] = '\0';
- msg_hash_get_help_enum(
- MENU_ENUM_LABEL_VALUE_MENU_ENUM_CONTROLS_PROLOG,
- s2, sizeof(s2));
- snprintf(s, len,
- "%s"
- "[%s]: "
- "%-20s\n"
- "[%s]: "
- "%-20s\n"
- "[%s]: "
- "%-20s\n"
- "[%s]: "
- "%-20s\n"
- "[%s]: "
- "%-20s\n"
- "[%s]: "
- "%-20s\n"
- "[%s]: "
- "%-20s\n"
- "[%s]: "
- "%-20s\n"
- "[%s]: "
- "%-20s\n",
- s2,
- msg_hash_to_str(
- MENU_ENUM_LABEL_VALUE_BASIC_MENU_CONTROLS_SCROLL_UP),
- desc[0],
- msg_hash_to_str(
- MENU_ENUM_LABEL_VALUE_BASIC_MENU_CONTROLS_SCROLL_DOWN),
- desc[1],
- msg_hash_to_str(
- MENU_ENUM_LABEL_VALUE_BASIC_MENU_CONTROLS_CONFIRM),
- desc[2],
- msg_hash_to_str(
- MENU_ENUM_LABEL_VALUE_BASIC_MENU_CONTROLS_BACK),
- desc[3],
- msg_hash_to_str(
- MENU_ENUM_LABEL_VALUE_BASIC_MENU_CONTROLS_INFO),
- desc[4],
- msg_hash_to_str(
- MENU_ENUM_LABEL_VALUE_BASIC_MENU_CONTROLS_START),
- desc[5],
- msg_hash_to_str(
- MENU_ENUM_LABEL_VALUE_BASIC_MENU_CONTROLS_TOGGLE_MENU),
- desc[6],
- msg_hash_to_str(
- MENU_ENUM_LABEL_VALUE_BASIC_MENU_CONTROLS_QUIT),
- desc[7],
- msg_hash_to_str(
- MENU_ENUM_LABEL_VALUE_BASIC_MENU_CONTROLS_TOGGLE_KEYBOARD),
- desc[8]
- );
- }
- break;
- #ifdef HAVE_CHEEVOS
- case MENU_DIALOG_HELP_CHEEVOS_DESCRIPTION:
- {
- rcheevos_ctx_desc_t desc_info;
- desc_info.idx = p_dialog->current_id;
- desc_info.s = s;
- desc_info.len = len;
- rcheevos_get_description((rcheevos_ctx_desc_t*) &desc_info);
- }
- break;
- #endif
- case MENU_DIALOG_HELP_WHAT_IS_A_CORE:
- msg_hash_get_help_enum(MENU_ENUM_LABEL_VALUE_WHAT_IS_A_CORE_DESC,
- s, len);
- break;
- case MENU_DIALOG_HELP_LOADING_CONTENT:
- msg_hash_get_help_enum(MENU_ENUM_LABEL_LOAD_CONTENT_LIST,
- s, len);
- break;
- case MENU_DIALOG_HELP_CHANGE_VIRTUAL_GAMEPAD:
- msg_hash_get_help_enum(
- MENU_ENUM_LABEL_VALUE_HELP_CHANGE_VIRTUAL_GAMEPAD_DESC,
- s, len);
- break;
- case MENU_DIALOG_HELP_AUDIO_VIDEO_TROUBLESHOOTING:
- msg_hash_get_help_enum(
- MENU_ENUM_LABEL_VALUE_HELP_AUDIO_VIDEO_TROUBLESHOOTING_DESC,
- s, len);
- break;
- case MENU_DIALOG_HELP_SEND_DEBUG_INFO:
- msg_hash_get_help_enum(
- MENU_ENUM_LABEL_VALUE_HELP_SEND_DEBUG_INFO_DESC,
- s, len);
- break;
- case MENU_DIALOG_HELP_SCANNING_CONTENT:
- msg_hash_get_help_enum(MENU_ENUM_LABEL_VALUE_HELP_SCANNING_CONTENT_DESC,
- s, len);
- break;
- case MENU_DIALOG_HELP_EXTRACT:
- {
- struct rarch_state *p_rarch = &rarch_st;
- settings_t *settings = p_rarch->configuration_settings;
- bool bundle_finished = settings->bools.bundle_finished;
- msg_hash_get_help_enum(
- MENU_ENUM_LABEL_VALUE_EXTRACTING_PLEASE_WAIT,
- s, len);
- if (bundle_finished)
- {
- configuration_set_bool(settings,
- settings->bools.bundle_finished, false);
- p_dialog->current_type = MENU_DIALOG_NONE;
- return 1;
- }
- }
- break;
- case MENU_DIALOG_QUIT_CONFIRM:
- case MENU_DIALOG_INFORMATION:
- case MENU_DIALOG_QUESTION:
- case MENU_DIALOG_WARNING:
- case MENU_DIALOG_ERROR:
- msg_hash_get_help_enum(MSG_UNKNOWN,
- s, len);
- break;
- case MENU_DIALOG_NONE:
- default:
- break;
- }
- return 0;
- }
- void menu_dialog_unset_pending_push(void)
- {
- struct rarch_state *p_rarch = &rarch_st;
- menu_dialog_t *p_dialog = &p_rarch->dialog_st;
- p_dialog->pending_push = false;
- }
- void menu_dialog_push_pending(enum menu_dialog_type type)
- {
- struct rarch_state *p_rarch = &rarch_st;
- menu_dialog_t *p_dialog = &p_rarch->dialog_st;
- p_dialog->current_type = type;
- p_dialog->pending_push = true;
- }
- void menu_dialog_set_current_id(unsigned id)
- {
- struct rarch_state *p_rarch = &rarch_st;
- menu_dialog_t *p_dialog = &p_rarch->dialog_st;
- p_dialog->current_id = id;
- }
- void input_keyboard_mapping_bits(unsigned mode, unsigned key)
- {
- struct rarch_state *p_rarch = &rarch_st;
- switch (mode)
- {
- case 0:
- BIT512_CLEAR_PTR(&p_rarch->keyboard_mapping_bits, key);
- break;
- case 1:
- BIT512_SET_PTR(&p_rarch->keyboard_mapping_bits, key);
- break;
- default:
- break;
- }
- }
- static bool menu_input_key_bind_custom_bind_keyboard_cb(
- void *data, unsigned code)
- {
- uint64_t current_usec;
- struct rarch_state *p_rarch = &rarch_st;
- settings_t *settings = p_rarch->configuration_settings;
- struct menu_bind_state *binds = &p_rarch->menu_input_binds;
- uint64_t input_bind_hold_us = settings->uints.input_bind_hold * 1000000;
- uint64_t input_bind_timeout_us = settings->uints.input_bind_timeout * 1000000;
- /* Clear old mapping bit */
- input_keyboard_mapping_bits(0, binds->buffer.key);
- /* store key in bind */
- binds->buffer.key = (enum retro_key)code;
- /* Store new mapping bit */
- input_keyboard_mapping_bits(1, binds->buffer.key);
- /* write out the bind */
- *(binds->output) = binds->buffer;
- /* next bind */
- binds->begin++;
- binds->output++;
- binds->buffer =* (binds->output);
- current_usec = cpu_features_get_time_usec();
- RARCH_TIMER_BEGIN_NEW_TIME_USEC(
- binds->timer_hold,
- current_usec,
- input_bind_hold_us);
- RARCH_TIMER_BEGIN_NEW_TIME_USEC(
- binds->timer_timeout,
- current_usec, input_bind_timeout_us);
- return (binds->begin <= binds->last);
- }
- static int menu_input_key_bind_set_mode_common(
- struct menu_state *menu_st,
- struct menu_bind_state *binds,
- enum menu_input_binds_ctl_state state,
- rarch_setting_t *setting)
- {
- menu_displaylist_info_t info;
- unsigned bind_type = 0;
- struct retro_keybind *keybind = NULL;
- unsigned index_offset = setting->index_offset;
- menu_list_t *menu_list = menu_st->entries.list;
- file_list_t *menu_stack = menu_list ? MENU_LIST_GET(menu_list, (unsigned)0) : NULL;
- size_t selection = menu_st->selection_ptr;
- menu_displaylist_info_init(&info);
- switch (state)
- {
- case MENU_INPUT_BINDS_CTL_BIND_SINGLE:
- keybind = (struct retro_keybind*)setting->value.target.keybind;
- if (!keybind)
- return -1;
- bind_type = setting_get_bind_type(setting);
- binds->begin = bind_type;
- binds->last = bind_type;
- binds->output = keybind;
- binds->buffer = *(binds->output);
- binds->user = index_offset;
- info.list = menu_stack;
- info.type = MENU_SETTINGS_CUSTOM_BIND_KEYBOARD;
- info.directory_ptr = selection;
- info.enum_idx = MENU_ENUM_LABEL_CUSTOM_BIND;
- info.label = strdup(
- msg_hash_to_str(MENU_ENUM_LABEL_CUSTOM_BIND));
- break;
- case MENU_INPUT_BINDS_CTL_BIND_ALL:
- binds->output = &input_config_binds[index_offset][0];
- binds->buffer = *(binds->output);
- binds->begin = MENU_SETTINGS_BIND_BEGIN;
- binds->last = MENU_SETTINGS_BIND_LAST;
- info.list = menu_stack;
- info.type = MENU_SETTINGS_CUSTOM_BIND_KEYBOARD;
- info.directory_ptr = selection;
- info.enum_idx = MENU_ENUM_LABEL_CUSTOM_BIND_ALL;
- info.label = strdup(
- msg_hash_to_str(MENU_ENUM_LABEL_CUSTOM_BIND_ALL));
- break;
- default:
- case MENU_INPUT_BINDS_CTL_BIND_NONE:
- return 0;
- }
- if (menu_displaylist_ctl(DISPLAYLIST_INFO, &info))
- menu_displaylist_process(&info);
- menu_displaylist_info_free(&info);
- return 0;
- }
- static void menu_input_key_bind_poll_bind_get_rested_axes(
- const input_device_driver_t *joypad,
- const input_device_driver_t *sec_joypad,
- struct menu_bind_state *state)
- {
- unsigned a;
- unsigned port = state->port;
- if (joypad)
- {
- /* poll only the relevant port */
- for (a = 0; a < MENU_MAX_AXES; a++)
- {
- if (AXIS_POS(a) != AXIS_NONE)
- state->axis_state[port].rested_axes[a] =
- joypad->axis(port, AXIS_POS(a));
- if (AXIS_NEG(a) != AXIS_NONE)
- state->axis_state[port].rested_axes[a] +=
- joypad->axis(port, AXIS_NEG(a));
- }
- }
- if (sec_joypad)
- {
- /* poll only the relevant port */
- for (a = 0; a < MENU_MAX_AXES; a++)
- {
- if (AXIS_POS(a) != AXIS_NONE)
- state->axis_state[port].rested_axes[a] = sec_joypad->axis(port, AXIS_POS(a));
- if (AXIS_NEG(a) != AXIS_NONE)
- state->axis_state[port].rested_axes[a] += sec_joypad->axis(port, AXIS_NEG(a));
- }
- }
- }
- static void menu_input_key_bind_poll_bind_state_internal(
- const input_device_driver_t *joypad,
- struct menu_bind_state *state,
- unsigned port,
- bool timed_out)
- {
- unsigned i;
- /* poll only the relevant port */
- for (i = 0; i < MENU_MAX_BUTTONS; i++)
- state->state[port].buttons[i] = joypad->button(port, i);
- for (i = 0; i < MENU_MAX_AXES; i++)
- {
- if (AXIS_POS(i) != AXIS_NONE)
- state->state[port].axes[i] = joypad->axis(port, AXIS_POS(i));
- if (AXIS_NEG(i) != AXIS_NONE)
- state->state[port].axes[i] += joypad->axis(port, AXIS_NEG(i));
- }
- for (i = 0; i < MENU_MAX_HATS; i++)
- {
- if (joypad->button(port, HAT_MAP(i, HAT_UP_MASK)))
- state->state[port].hats[i] |= HAT_UP_MASK;
- if (joypad->button(port, HAT_MAP(i, HAT_DOWN_MASK)))
- state->state[port].hats[i] |= HAT_DOWN_MASK;
- if (joypad->button(port, HAT_MAP(i, HAT_LEFT_MASK)))
- state->state[port].hats[i] |= HAT_LEFT_MASK;
- if (joypad->button(port, HAT_MAP(i, HAT_RIGHT_MASK)))
- state->state[port].hats[i] |= HAT_RIGHT_MASK;
- }
- }
- static void menu_input_key_bind_poll_bind_state(
- struct rarch_state *p_rarch,
- struct menu_bind_state *state,
- bool timed_out)
- {
- unsigned b;
- rarch_joypad_info_t joypad_info;
- input_driver_t *current_input = p_rarch->current_input;
- void *input_data = p_rarch->current_input_data;
- unsigned port = state->port;
- const input_device_driver_t *joypad = p_rarch->joypad;
- #ifdef HAVE_MFI
- const input_device_driver_t *sec_joypad = p_rarch->sec_joypad;
- #else
- const input_device_driver_t *sec_joypad = NULL;
- #endif
- memset(state->state, 0, sizeof(state->state));
- /* poll mouse (on the relevant port) */
- for (b = 0; b < MENU_MAX_MBUTTONS; b++)
- state->state[port].mouse_buttons[b] =
- input_mouse_button_raw(p_rarch,
- p_rarch->configuration_settings, port, b);
- joypad_info.joy_idx = 0;
- joypad_info.auto_binds = NULL;
- joypad_info.axis_threshold = 0.0f;
- state->skip = timed_out;
- if (current_input->input_state)
- state->skip |=
- current_input->input_state(
- input_data,
- joypad,
- sec_joypad,
- &joypad_info,
- NULL,
- p_rarch->keyboard_mapping_blocked,
- 0,
- RETRO_DEVICE_KEYBOARD,
- 0,
- RETROK_RETURN);
- if (joypad)
- {
- if (joypad->poll)
- joypad->poll();
- menu_input_key_bind_poll_bind_state_internal(
- joypad, state, port, timed_out);
- }
- if (sec_joypad)
- {
- if (sec_joypad->poll)
- sec_joypad->poll();
- menu_input_key_bind_poll_bind_state_internal(
- sec_joypad, state, port, timed_out);
- }
- }
- static bool menu_input_key_bind_poll_find_trigger_pad(
- struct menu_bind_state *state,
- struct menu_bind_state *new_state,
- struct retro_keybind * output,
- unsigned p)
- {
- unsigned a, b, h;
- const struct menu_bind_state_port *n = (const struct menu_bind_state_port*)
- &new_state->state[p];
- const struct menu_bind_state_port *o = (const struct menu_bind_state_port*)
- &state->state[p];
- for (b = 0; b < MENU_MAX_MBUTTONS; b++)
- {
- bool iterate = n->mouse_buttons[b] && !o->mouse_buttons[b];
- if (!iterate)
- continue;
- switch (b)
- {
- case RETRO_DEVICE_ID_MOUSE_LEFT:
- case RETRO_DEVICE_ID_MOUSE_RIGHT:
- case RETRO_DEVICE_ID_MOUSE_MIDDLE:
- case RETRO_DEVICE_ID_MOUSE_BUTTON_4:
- case RETRO_DEVICE_ID_MOUSE_BUTTON_5:
- case RETRO_DEVICE_ID_MOUSE_WHEELUP:
- case RETRO_DEVICE_ID_MOUSE_WHEELDOWN:
- case RETRO_DEVICE_ID_MOUSE_HORIZ_WHEELUP:
- case RETRO_DEVICE_ID_MOUSE_HORIZ_WHEELDOWN:
- output->mbutton = b;
- return true;
- }
- }
- for (b = 0; b < MENU_MAX_BUTTONS; b++)
- {
- bool iterate = n->buttons[b] && !o->buttons[b];
- if (!iterate)
- continue;
- output->joykey = b;
- output->joyaxis = AXIS_NONE;
- return true;
- }
- /* Axes are a bit tricky ... */
- for (a = 0; a < MENU_MAX_AXES; a++)
- {
- int locked_distance = abs(n->axes[a] -
- new_state->axis_state[p].locked_axes[a]);
- int rested_distance = abs(n->axes[a] -
- new_state->axis_state[p].rested_axes[a]);
- if (abs(n->axes[a]) >= 20000 &&
- locked_distance >= 20000 &&
- rested_distance >= 20000)
- {
- /* Take care of case where axis rests on +/- 0x7fff
- * (e.g. 360 controller on Linux) */
- output->joyaxis = n->axes[a] > 0
- ? AXIS_POS(a) : AXIS_NEG(a);
- output->joykey = NO_BTN;
- /* Lock the current axis */
- new_state->axis_state[p].locked_axes[a] =
- n->axes[a] > 0 ?
- 0x7fff : -0x7fff;
- return true;
- }
- if (locked_distance >= 20000) /* Unlock the axis. */
- new_state->axis_state[p].locked_axes[a] = 0;
- }
- for (h = 0; h < MENU_MAX_HATS; h++)
- {
- uint16_t trigged = n->hats[h] & (~o->hats[h]);
- uint16_t sane_trigger = 0;
- if (trigged & HAT_UP_MASK)
- sane_trigger = HAT_UP_MASK;
- else if (trigged & HAT_DOWN_MASK)
- sane_trigger = HAT_DOWN_MASK;
- else if (trigged & HAT_LEFT_MASK)
- sane_trigger = HAT_LEFT_MASK;
- else if (trigged & HAT_RIGHT_MASK)
- sane_trigger = HAT_RIGHT_MASK;
- if (sane_trigger)
- {
- output->joykey = HAT_MAP(h, sane_trigger);
- output->joyaxis = AXIS_NONE;
- return true;
- }
- }
- return false;
- }
- #ifdef ANDROID
- static bool menu_input_key_bind_poll_find_hold_pad(
- struct menu_bind_state *new_state,
- struct retro_keybind * output,
- unsigned p)
- {
- unsigned a, b, h;
- const struct menu_bind_state_port *n =
- (const struct menu_bind_state_port*)
- &new_state->state[p];
- for (b = 0; b < MENU_MAX_MBUTTONS; b++)
- {
- bool iterate = n->mouse_buttons[b];
- if (!iterate)
- continue;
- switch (b)
- {
- case RETRO_DEVICE_ID_MOUSE_LEFT:
- case RETRO_DEVICE_ID_MOUSE_RIGHT:
- case RETRO_DEVICE_ID_MOUSE_MIDDLE:
- case RETRO_DEVICE_ID_MOUSE_BUTTON_4:
- case RETRO_DEVICE_ID_MOUSE_BUTTON_5:
- case RETRO_DEVICE_ID_MOUSE_WHEELUP:
- case RETRO_DEVICE_ID_MOUSE_WHEELDOWN:
- case RETRO_DEVICE_ID_MOUSE_HORIZ_WHEELUP:
- case RETRO_DEVICE_ID_MOUSE_HORIZ_WHEELDOWN:
- output->mbutton = b;
- return true;
- }
- }
- for (b = 0; b < MENU_MAX_BUTTONS; b++)
- {
- bool iterate = n->buttons[b];
- if (!iterate)
- continue;
- output->joykey = b;
- output->joyaxis = AXIS_NONE;
- return true;
- }
- /* Axes are a bit tricky ... */
- for (a = 0; a < MENU_MAX_AXES; a++)
- {
- if (abs(n->axes[a]) >= 20000)
- {
- /* Take care of case where axis rests on +/- 0x7fff
- * (e.g. 360 controller on Linux) */
- output->joyaxis = n->axes[a] > 0
- ? AXIS_POS(a) : AXIS_NEG(a);
- output->joykey = NO_BTN;
- return true;
- }
- }
- for (h = 0; h < MENU_MAX_HATS; h++)
- {
- uint16_t trigged = n->hats[h];
- uint16_t sane_trigger = 0;
- if (trigged & HAT_UP_MASK)
- sane_trigger = HAT_UP_MASK;
- else if (trigged & HAT_DOWN_MASK)
- sane_trigger = HAT_DOWN_MASK;
- else if (trigged & HAT_LEFT_MASK)
- sane_trigger = HAT_LEFT_MASK;
- else if (trigged & HAT_RIGHT_MASK)
- sane_trigger = HAT_RIGHT_MASK;
- if (sane_trigger)
- {
- output->joykey = HAT_MAP(h, sane_trigger);
- output->joyaxis = AXIS_NONE;
- return true;
- }
- }
- return false;
- }
- #endif
- static bool menu_input_key_bind_poll_find_trigger(
- unsigned max_users,
- struct menu_bind_state *state,
- struct menu_bind_state *new_state,
- struct retro_keybind * output)
- {
- if (state && new_state)
- {
- unsigned i;
- for (i = 0; i < max_users; i++)
- {
- if (menu_input_key_bind_poll_find_trigger_pad(
- state, new_state, output, i))
- return true;
- }
- }
- return false;
- }
- #ifdef ANDROID
- static bool menu_input_key_bind_poll_find_hold(
- unsigned max_users,
- struct menu_bind_state *new_state,
- struct retro_keybind * output)
- {
- if (new_state)
- {
- unsigned i;
- for (i = 0; i < max_users; i++)
- {
- if (menu_input_key_bind_poll_find_hold_pad(new_state, output, i))
- return true;
- }
- }
- return false;
- }
- #endif
- bool menu_input_key_bind_set_mode(
- enum menu_input_binds_ctl_state state, void *data)
- {
- uint64_t current_usec;
- unsigned index_offset;
- rarch_setting_t *setting = (rarch_setting_t*)data;
- struct rarch_state *p_rarch = &rarch_st;
- menu_handle_t *menu = p_rarch->menu_driver_data;
- menu_input_t *menu_input = &p_rarch->menu_input_state;
- settings_t *settings = p_rarch->configuration_settings;
- struct menu_bind_state *binds = &p_rarch->menu_input_binds;
- uint64_t input_bind_hold_us = settings->uints.input_bind_hold
- * 1000000;
- uint64_t input_bind_timeout_us = settings->uints.input_bind_timeout
- * 1000000;
- if (!setting || !menu)
- return false;
- if (menu_input_key_bind_set_mode_common(&p_rarch->menu_driver_state,
- binds, state, setting) == -1)
- return false;
- index_offset = setting->index_offset;
- binds->port = settings->uints.input_joypad_map[index_offset];
- menu_input_key_bind_poll_bind_get_rested_axes(
- p_rarch->joypad,
- #ifdef HAVE_MFI
- p_rarch->sec_joypad,
- #else
- NULL,
- #endif
- binds);
- menu_input_key_bind_poll_bind_state(p_rarch,
- binds, false);
- current_usec = cpu_features_get_time_usec();
- RARCH_TIMER_BEGIN_NEW_TIME_USEC(
- binds->timer_hold,
- current_usec,
- input_bind_hold_us);
- RARCH_TIMER_BEGIN_NEW_TIME_USEC(
- binds->timer_timeout,
- current_usec,
- input_bind_timeout_us);
- p_rarch->keyboard_press_cb =
- menu_input_key_bind_custom_bind_keyboard_cb;
- p_rarch->keyboard_press_data = menu;
- /* While waiting for input, we have to block all hotkeys. */
- p_rarch->keyboard_mapping_blocked = true;
- /* Upon triggering an input bind operation,
- * pointer input must be inhibited - otherwise
- * attempting to bind mouse buttons will cause
- * spurious menu actions */
- menu_input->select_inhibit = true;
- menu_input->cancel_inhibit = true;
- return true;
- }
- bool menu_input_key_bind_set_min_max(menu_input_ctx_bind_limits_t *lim)
- {
- struct rarch_state *p_rarch = &rarch_st;
- struct menu_bind_state *binds = &p_rarch->menu_input_binds;
- if (!lim)
- return false;
- binds->begin = lim->min;
- binds->last = lim->max;
- return true;
- }
- static bool menu_input_key_bind_iterate(
- struct rarch_state *p_rarch,
- menu_input_ctx_bind_t *bind,
- retro_time_t current_time)
- {
- bool timed_out = false;
- settings_t *settings = p_rarch->configuration_settings;
- struct menu_bind_state *_binds = &p_rarch->menu_input_binds;
- menu_input_t *menu_input = &p_rarch->menu_input_state;
- uint64_t input_bind_hold_us = settings->uints.input_bind_hold * 1000000;
- uint64_t input_bind_timeout_us = settings->uints.input_bind_timeout * 1000000;
- snprintf(bind->s, bind->len,
- "[%s]\nPress keyboard, mouse or joypad\n(Timeout %d %s)",
- input_config_bind_map_get_desc(
- _binds->begin - MENU_SETTINGS_BIND_BEGIN),
- RARCH_TIMER_GET_TIMEOUT(_binds->timer_timeout),
- msg_hash_to_str(MENU_ENUM_LABEL_VALUE_SECONDS));
- /* Tick main timers */
- RARCH_TIMER_TICK(_binds->timer_timeout, current_time);
- RARCH_TIMER_TICK(_binds->timer_hold, current_time);
- if (RARCH_TIMER_HAS_EXPIRED(_binds->timer_timeout))
- {
- uint64_t current_usec = cpu_features_get_time_usec();
- p_rarch->keyboard_mapping_blocked = false;
- /*skip to next bind*/
- _binds->begin++;
- _binds->output++;
- RARCH_TIMER_BEGIN_NEW_TIME_USEC(_binds->timer_hold,
- current_usec,
- input_bind_hold_us);
- RARCH_TIMER_BEGIN_NEW_TIME_USEC(_binds->timer_timeout,
- current_usec,
- input_bind_timeout_us);
- timed_out = true;
- }
- /* binds.begin is updated in keyboard_press callback. */
- if (_binds->begin > _binds->last)
- {
- /* Avoid new binds triggering things right away. */
- /* Inhibits input for 2 frames
- * > Required, since input is ignored for 1 frame
- * after certain events - e.g. closing the OSK */
- p_rarch->input_driver_flushing_input = 2;
- /* We won't be getting any key events, so just cancel early. */
- if (timed_out)
- {
- p_rarch->keyboard_press_cb = NULL;
- p_rarch->keyboard_press_data = NULL;
- p_rarch->keyboard_mapping_blocked = false;
- }
- return true;
- }
- {
- bool complete = false;
- struct menu_bind_state new_binds = *_binds;
- p_rarch->keyboard_mapping_blocked = false;
- menu_input_key_bind_poll_bind_state(p_rarch,
- &new_binds, timed_out);
- #ifdef ANDROID
- /* Keep resetting bind during the hold period,
- * or we'll potentially bind joystick and mouse, etc.*/
- new_binds.buffer = *(new_binds.output);
- if (menu_input_key_bind_poll_find_hold(
- p_rarch->input_driver_max_users,
- &new_binds, &new_binds.buffer))
- {
- uint64_t current_usec = cpu_features_get_time_usec();
- /* Inhibit timeout*/
- RARCH_TIMER_BEGIN_NEW_TIME_USEC(
- new_binds.timer_timeout,
- current_usec,
- input_bind_timeout_us);
- /* Run hold timer*/
- RARCH_TIMER_TICK(new_binds.timer_hold, current_time);
- snprintf(bind->s, bind->len,
- "[%s]\npress keyboard, mouse or joypad\nand hold ...",
- input_config_bind_map_get_desc(
- _binds->begin - MENU_SETTINGS_BIND_BEGIN));
- /* Hold complete? */
- if (RARCH_TIMER_HAS_EXPIRED(new_binds.timer_hold))
- complete = true;
- }
- else
- {
- uint64_t current_usec = cpu_features_get_time_usec();
- /* Reset hold countdown*/
- RARCH_TIMER_BEGIN_NEW_TIME_USEC(new_binds.timer_hold,
- current_usec,
- input_bind_hold_us);
- }
- #else
- if ((new_binds.skip && !_binds->skip) ||
- menu_input_key_bind_poll_find_trigger(
- p_rarch->input_driver_max_users,
- _binds, &new_binds, &(new_binds.buffer)))
- complete = true;
- #endif
- if (complete)
- {
- uint64_t current_usec = cpu_features_get_time_usec();
- /* Update bind */
- *(new_binds.output) = new_binds.buffer;
- p_rarch->keyboard_mapping_blocked = false;
- /* Avoid new binds triggering things right away. */
- /* Inhibits input for 2 frames
- * > Required, since input is ignored for 1 frame
- * after certain events - e.g. closing the OSK */
- p_rarch->input_driver_flushing_input = 2;
- new_binds.begin++;
- if (new_binds.begin > new_binds.last)
- {
- p_rarch->keyboard_press_cb = NULL;
- p_rarch->keyboard_press_data = NULL;
- p_rarch->keyboard_mapping_blocked = false;
- return true;
- }
- /*next bind*/
- new_binds.output++;
- new_binds.buffer = *(new_binds.output);
- RARCH_TIMER_BEGIN_NEW_TIME_USEC(new_binds.timer_hold,
- current_usec, input_bind_hold_us);
- RARCH_TIMER_BEGIN_NEW_TIME_USEC(new_binds.timer_timeout,
- current_usec, input_bind_timeout_us);
- }
- *(_binds) = new_binds;
- }
- /* Pointer input must be inhibited on each
- * frame that the bind operation is active -
- * otherwise attempting to bind mouse buttons
- * will cause spurious menu actions */
- menu_input->select_inhibit = true;
- menu_input->cancel_inhibit = true;
- return false;
- }
- /* This sets up all the callback functions for a menu entry.
- *
- * OK : When we press the 'OK' button on an entry.
- * Cancel : When we press the 'Cancel' button on an entry.
- * Scan : When we press the 'Scan' button on an entry.
- * Start : When we press the 'Start' button on an entry.
- * Select : When we press the 'Select' button on an entry.
- * Info : When we press the 'Info' button on an entry.
- * Content Switch : ??? (TODO/FIXME - Kivutar should document this)
- * Up : when we press 'Up' on the D-pad while this entry is selected.
- * Down : when we press 'Down' on the D-pad while this entry is selected.
- * Left : when we press 'Left' on the D-pad while this entry is selected.
- * Right : when we press 'Right' on the D-pad while this entry is selected.
- * Deferred push : When pressing an entry results in spawning a new list, it waits until the next
- * frame to push this onto the stack. This function callback will then be invoked.
- * Refresh : What happens when the screen has to be refreshed. Does an entry have internal state
- * that needs to be rebuild?
- * Get value: Each entry has associated 'text', which we call the value. This function callback
- * lets us render that text.
- * Get title: Each entry can have a custom 'title'.
- * Label: Each entry has a label name. This function callback lets us render that label text.
- * Sublabel: each entry has a sublabel, which consists of one or more lines of additional information.
- * This function callback lets us render that text.
- */
- static void menu_cbs_init(
- struct menu_state *menu_st,
- const menu_ctx_driver_t *menu_driver_ctx,
- file_list_t *list,
- menu_file_list_cbs_t *cbs,
- const char *path, const char *label,
- unsigned type, size_t idx)
- {
- const char *menu_label = NULL;
- file_list_t *menu_list = MENU_LIST_GET(menu_st->entries.list, 0);
- #ifdef DEBUG_LOG
- menu_file_list_cbs_t *menu_cbs = (menu_file_list_cbs_t*)
- menu_list->list[list->size - 1].actiondata;
- enum msg_hash_enums enum_idx = menu_cbs ? menu_cbs->enum_idx : MSG_UNKNOWN;
- #endif
- if (menu_list && menu_list->size)
- file_list_get_at_offset(menu_list, menu_list->size - 1, NULL, &menu_label, NULL, NULL);
- if (!label || !menu_label)
- return;
- #ifdef DEBUG_LOG
- RARCH_LOG("\n");
- if (cbs && cbs->enum_idx != MSG_UNKNOWN)
- RARCH_LOG("\t\t\tenum_idx %d [%s]\n", cbs->enum_idx, msg_hash_to_str(cbs->enum_idx));
- #endif
- /* It will try to find a corresponding callback function inside
- * menu_cbs_ok.c, then map this callback to the entry. */
- menu_cbs_init_bind_ok(cbs, path, label, type, idx, menu_label);
- /* It will try to find a corresponding callback function inside
- * menu_cbs_cancel.c, then map this callback to the entry. */
- menu_cbs_init_bind_cancel(cbs, path, label, type, idx);
- /* It will try to find a corresponding callback function inside
- * menu_cbs_scan.c, then map this callback to the entry. */
- menu_cbs_init_bind_scan(cbs, path, label, type, idx);
- /* It will try to find a corresponding callback function inside
- * menu_cbs_start.c, then map this callback to the entry. */
- menu_cbs_init_bind_start(cbs, path, label, type, idx);
- /* It will try to find a corresponding callback function inside
- * menu_cbs_select.c, then map this callback to the entry. */
- menu_cbs_init_bind_select(cbs, path, label, type, idx);
- /* It will try to find a corresponding callback function inside
- * menu_cbs_info.c, then map this callback to the entry. */
- menu_cbs_init_bind_info(cbs, path, label, type, idx);
- /* It will try to find a corresponding callback function inside
- * menu_cbs_left.c, then map this callback to the entry. */
- menu_cbs_init_bind_left(cbs, path, label, type, idx, menu_label);
- /* It will try to find a corresponding callback function inside
- * menu_cbs_right.c, then map this callback to the entry. */
- menu_cbs_init_bind_right(cbs, path, label, type, idx, menu_label);
- /* It will try to find a corresponding callback function inside
- * menu_cbs_deferred_push.c, then map this callback to the entry. */
- menu_cbs_init_bind_deferred_push(cbs, path, label, type, idx);
- /* It will try to find a corresponding callback function inside
- * menu_cbs_get_string_representation.c, then map this callback to the entry. */
- menu_cbs_init_bind_get_string_representation(cbs, path, label, type, idx);
- /* It will try to find a corresponding callback function inside
- * menu_cbs_title.c, then map this callback to the entry. */
- menu_cbs_init_bind_title(cbs, path, label, type, idx);
- /* It will try to find a corresponding callback function inside
- * menu_cbs_label.c, then map this callback to the entry. */
- menu_cbs_init_bind_label(cbs, path, label, type, idx);
- /* It will try to find a corresponding callback function inside
- * menu_cbs_sublabel.c, then map this callback to the entry. */
- menu_cbs_init_bind_sublabel(cbs, path, label, type, idx);
- if (menu_driver_ctx && menu_driver_ctx->bind_init)
- menu_driver_ctx->bind_init(
- cbs,
- path,
- label,
- type,
- idx);
- }
- /* Pretty much a stub function. TODO/FIXME - Might as well remove this. */
- int menu_cbs_exit(void)
- {
- return -1;
- }
- static enum action_iterate_type action_iterate_type(const char *label)
- {
- if (string_is_equal(label, "info_screen"))
- return ITERATE_TYPE_INFO;
- if (string_starts_with_size(label, "help", STRLEN_CONST("help")))
- if (
- string_is_equal(label, "help") ||
- string_is_equal(label, "help_controls") ||
- string_is_equal(label, "help_what_is_a_core") ||
- string_is_equal(label, "help_loading_content") ||
- string_is_equal(label, "help_scanning_content") ||
- string_is_equal(label, "help_change_virtual_gamepad") ||
- string_is_equal(label, "help_audio_video_troubleshooting") ||
- string_is_equal(label, "help_send_debug_info")
- )
- return ITERATE_TYPE_HELP;
- if (string_is_equal(label, "cheevos_description"))
- return ITERATE_TYPE_HELP;
- if (string_starts_with_size(label, "custom_bind", STRLEN_CONST("custom_bind")))
- if (
- string_is_equal(label, "custom_bind") ||
- string_is_equal(label, "custom_bind_all") ||
- string_is_equal(label, "custom_bind_defaults")
- )
- return ITERATE_TYPE_BIND;
- return ITERATE_TYPE_DEFAULT;
- }
- #ifdef HAVE_ACCESSIBILITY
- static void get_current_menu_value(struct menu_state *menu_st,
- char *s, size_t len)
- {
- menu_entry_t entry;
- const char* entry_label;
- MENU_ENTRY_INIT(entry);
- entry.path_enabled = false;
- entry.label_enabled = false;
- entry.rich_label_enabled = false;
- entry.sublabel_enabled = false;
- menu_entry_get(&entry, 0, menu_st->selection_ptr, NULL, true);
- if (entry.enum_idx == MENU_ENUM_LABEL_CHEEVOS_PASSWORD)
- entry_label = entry.password_value;
- else
- entry_label = entry.value;
- strlcpy(s, entry_label, len);
- }
- static void get_current_menu_label(struct menu_state *menu_st,
- char *s, size_t len)
- {
- menu_entry_t entry;
- const char* entry_label;
- MENU_ENTRY_INIT(entry);
- menu_entry_get(&entry, 0, menu_st->selection_ptr, NULL, true);
- if (!string_is_empty(entry.rich_label))
- entry_label = entry.rich_label;
- else
- entry_label = entry.path;
- strlcpy(s, entry_label, len);
- }
- static void get_current_menu_sublabel(struct menu_state *menu_st,
- char *s, size_t len)
- {
- menu_entry_t entry;
- MENU_ENTRY_INIT(entry);
- entry.path_enabled = false;
- entry.label_enabled = false;
- entry.rich_label_enabled = false;
- entry.value_enabled = false;
- menu_entry_get(&entry, 0, menu_st->selection_ptr, NULL, true);
- strlcpy(s, entry.sublabel, len);
- }
- #endif
- static void menu_input_set_pointer_visibility(
- menu_input_pointer_hw_state_t *pointer_hw_state,
- menu_input_t *menu_input,
- retro_time_t current_time)
- {
- static bool cursor_shown = false;
- static bool cursor_hidden = false;
- static retro_time_t end_time = 0;
- /* Ensure that mouse cursor is hidden when not in use */
- if ((menu_input->pointer.type == MENU_POINTER_MOUSE)
- && pointer_hw_state->active)
- {
- /* Show cursor */
- if ((current_time > end_time) && !cursor_shown)
- {
- menu_ctx_environment_t menu_environ;
- menu_environ.type = MENU_ENVIRON_ENABLE_MOUSE_CURSOR;
- menu_environ.data = NULL;
- menu_driver_ctl(RARCH_MENU_CTL_ENVIRONMENT, &menu_environ);
- cursor_shown = true;
- cursor_hidden = false;
- }
- end_time = current_time + MENU_INPUT_HIDE_CURSOR_DELAY;
- }
- else
- {
- /* Hide cursor */
- if ((current_time > end_time) && !cursor_hidden)
- {
- menu_ctx_environment_t menu_environ;
- menu_environ.type = MENU_ENVIRON_DISABLE_MOUSE_CURSOR;
- menu_environ.data = NULL;
- menu_driver_ctl(RARCH_MENU_CTL_ENVIRONMENT, &menu_environ);
- cursor_shown = false;
- cursor_hidden = true;
- }
- }
- }
- /**
- * menu_iterate:
- * @input : input sample for this frame
- * @old_input : input sample of the previous frame
- * @trigger_input : difference' input sample - difference
- * between 'input' and 'old_input'
- *
- * Runs RetroArch menu for one frame.
- *
- * Returns: 0 on success, -1 if we need to quit out of the loop.
- **/
- static int generic_menu_iterate(
- struct rarch_state *p_rarch,
- menu_handle_t *menu,
- void *userdata, enum menu_action action,
- retro_time_t current_time)
- {
- #ifdef HAVE_ACCESSIBILITY
- static enum action_iterate_type
- last_iterate_type = ITERATE_TYPE_DEFAULT;
- #endif
- enum action_iterate_type iterate_type;
- unsigned file_type = 0;
- int ret = 0;
- const char *label = NULL;
- struct menu_state *menu_st = &p_rarch->menu_driver_state;
- gfx_display_t *p_disp = &p_rarch->dispgfx;
- gfx_animation_t *p_anim = &p_rarch->anim;
- file_list_t *list = MENU_LIST_GET(menu_st->entries.list, 0);
- if (list && list->size)
- file_list_get_at_offset(list, list->size - 1, NULL, &label, &file_type, NULL);
- menu->menu_state_msg[0] = '\0';
- iterate_type = action_iterate_type(label);
- p_rarch->menu_driver_is_binding = false;
- if ( action != MENU_ACTION_NOOP
- || MENU_ENTRIES_NEEDS_REFRESH(menu_st)
- || GFX_DISPLAY_GET_UPDATE_PENDING(p_anim, p_disp))
- {
- BIT64_SET(menu->state, MENU_STATE_RENDER_FRAMEBUFFER);
- }
- switch (iterate_type)
- {
- case ITERATE_TYPE_HELP:
- ret = menu_dialog_iterate(
- &p_rarch->dialog_st,
- menu->menu_state_msg, sizeof(menu->menu_state_msg),
- current_time);
- #ifdef HAVE_ACCESSIBILITY
- if ( (iterate_type != last_iterate_type)
- && is_accessibility_enabled(p_rarch))
- accessibility_speak_priority(p_rarch,
- menu->menu_state_msg, 10);
- #endif
- BIT64_SET(menu->state, MENU_STATE_RENDER_MESSAGEBOX);
- BIT64_SET(menu->state, MENU_STATE_POST_ITERATE);
- {
- bool pop_stack = false;
- if ( ret == 1 ||
- action == MENU_ACTION_OK ||
- action == MENU_ACTION_CANCEL
- )
- pop_stack = true;
- if (pop_stack)
- BIT64_SET(menu->state, MENU_STATE_POP_STACK);
- }
- break;
- case ITERATE_TYPE_BIND:
- {
- menu_input_ctx_bind_t bind;
- p_rarch->menu_driver_is_binding = true;
- bind.s = menu->menu_state_msg;
- bind.len = sizeof(menu->menu_state_msg);
- if (menu_input_key_bind_iterate(p_rarch,
- &bind, current_time))
- {
- size_t selection = menu_st->selection_ptr;
- menu_entries_pop_stack(&selection, 0, 0);
- menu_st->selection_ptr = selection;
- }
- else
- BIT64_SET(menu->state, MENU_STATE_RENDER_MESSAGEBOX);
- }
- break;
- case ITERATE_TYPE_INFO:
- {
- menu_list_t *menu_list = menu_st->entries.list;
- file_list_t *selection_buf = menu_list ? MENU_LIST_GET_SELECTION(menu_list, (unsigned)0) : NULL;
- size_t selection = menu_st->selection_ptr;
- menu_file_list_cbs_t *cbs = selection_buf ?
- (menu_file_list_cbs_t*)selection_buf->list[selection].actiondata
- : NULL;
- if (cbs && cbs->enum_idx != MSG_UNKNOWN)
- {
- /* Core updater/manager entries require special treatment */
- switch (cbs->enum_idx)
- {
- #ifdef HAVE_NETWORKING
- case MENU_ENUM_LABEL_CORE_UPDATER_ENTRY:
- {
- core_updater_list_t *core_list =
- core_updater_list_get_cached();
- const core_updater_list_entry_t *entry = NULL;
- const char *path =
- selection_buf->list[selection].path;
- /* Search for specified core */
- if (
- core_list
- && path
- && core_updater_list_get_filename(core_list,
- path, &entry)
- && !string_is_empty(entry->description)
- )
- strlcpy(menu->menu_state_msg, entry->description,
- sizeof(menu->menu_state_msg));
- else
- strlcpy(menu->menu_state_msg,
- msg_hash_to_str(MENU_ENUM_LABEL_VALUE_NO_INFORMATION_AVAILABLE),
- sizeof(menu->menu_state_msg));
- ret = 0;
- }
- break;
- #endif
- case MENU_ENUM_LABEL_CORE_MANAGER_ENTRY:
- {
- core_info_ctx_find_t core_info;
- const char *path = selection_buf->list[selection].path;
- /* Search for specified core */
- core_info.inf = NULL;
- core_info.path = path;
- if ( path
- && core_info_find(&core_info)
- && !string_is_empty(core_info.inf->description))
- strlcpy(menu->menu_state_msg,
- core_info.inf->description,
- sizeof(menu->menu_state_msg));
- else
- strlcpy(menu->menu_state_msg,
- msg_hash_to_str(MENU_ENUM_LABEL_VALUE_NO_INFORMATION_AVAILABLE),
- sizeof(menu->menu_state_msg));
- ret = 0;
- }
- break;
- default:
- ret = msg_hash_get_help_enum(cbs->enum_idx,
- menu->menu_state_msg, sizeof(menu->menu_state_msg));
- break;
- }
- #ifdef HAVE_ACCESSIBILITY
- if ( (iterate_type != last_iterate_type) &&
- is_accessibility_enabled(p_rarch))
- {
- if (string_is_equal(menu->menu_state_msg,
- msg_hash_to_str(
- MENU_ENUM_LABEL_VALUE_NO_INFORMATION_AVAILABLE)))
- {
- char current_sublabel[255];
- get_current_menu_sublabel(
- &p_rarch->menu_driver_state,
- current_sublabel, sizeof(current_sublabel));
- if (string_is_equal(current_sublabel, ""))
- accessibility_speak_priority(p_rarch,
- menu->menu_state_msg, 10);
- else
- accessibility_speak_priority(p_rarch,
- current_sublabel, 10);
- }
- else
- accessibility_speak_priority(p_rarch,
- menu->menu_state_msg, 10);
- }
- #endif
- }
- else
- {
- enum msg_hash_enums enum_idx = MSG_UNKNOWN;
- size_t selection = menu_st->selection_ptr;
- unsigned type = selection_buf->list[selection].type;
- switch (type)
- {
- case FILE_TYPE_FONT:
- case FILE_TYPE_VIDEO_FONT:
- enum_idx = MENU_ENUM_LABEL_FILE_BROWSER_FONT;
- break;
- case FILE_TYPE_RDB:
- enum_idx = MENU_ENUM_LABEL_FILE_BROWSER_RDB;
- break;
- case FILE_TYPE_OVERLAY:
- enum_idx = MENU_ENUM_LABEL_FILE_BROWSER_OVERLAY;
- break;
- #ifdef HAVE_VIDEO_LAYOUT
- case FILE_TYPE_VIDEO_LAYOUT:
- enum_idx = MENU_ENUM_LABEL_FILE_BROWSER_VIDEO_LAYOUT;
- break;
- #endif
- case FILE_TYPE_CHEAT:
- enum_idx = MENU_ENUM_LABEL_FILE_BROWSER_CHEAT;
- break;
- case FILE_TYPE_SHADER_PRESET:
- enum_idx = MENU_ENUM_LABEL_FILE_BROWSER_SHADER_PRESET;
- break;
- case FILE_TYPE_SHADER:
- enum_idx = MENU_ENUM_LABEL_FILE_BROWSER_SHADER;
- break;
- case FILE_TYPE_REMAP:
- enum_idx = MENU_ENUM_LABEL_FILE_BROWSER_REMAP;
- break;
- case FILE_TYPE_RECORD_CONFIG:
- enum_idx = MENU_ENUM_LABEL_FILE_BROWSER_RECORD_CONFIG;
- break;
- case FILE_TYPE_CURSOR:
- enum_idx = MENU_ENUM_LABEL_FILE_BROWSER_CURSOR;
- break;
- case FILE_TYPE_CONFIG:
- enum_idx = MENU_ENUM_LABEL_FILE_BROWSER_CONFIG;
- break;
- case FILE_TYPE_CARCHIVE:
- enum_idx = MENU_ENUM_LABEL_FILE_BROWSER_COMPRESSED_ARCHIVE;
- break;
- case FILE_TYPE_DIRECTORY:
- enum_idx = MENU_ENUM_LABEL_FILE_BROWSER_DIRECTORY;
- break;
- case FILE_TYPE_VIDEOFILTER: /* TODO/FIXME */
- case FILE_TYPE_AUDIOFILTER: /* TODO/FIXME */
- case FILE_TYPE_SHADER_SLANG: /* TODO/FIXME */
- case FILE_TYPE_SHADER_GLSL: /* TODO/FIXME */
- case FILE_TYPE_SHADER_HLSL: /* TODO/FIXME */
- case FILE_TYPE_SHADER_CG: /* TODO/FIXME */
- case FILE_TYPE_SHADER_PRESET_GLSLP: /* TODO/FIXME */
- case FILE_TYPE_SHADER_PRESET_HLSLP: /* TODO/FIXME */
- case FILE_TYPE_SHADER_PRESET_CGP: /* TODO/FIXME */
- case FILE_TYPE_SHADER_PRESET_SLANGP: /* TODO/FIXME */
- case FILE_TYPE_PLAIN:
- enum_idx = MENU_ENUM_LABEL_FILE_BROWSER_PLAIN_FILE;
- break;
- default:
- break;
- }
- if (enum_idx != MSG_UNKNOWN)
- ret = msg_hash_get_help_enum(enum_idx,
- menu->menu_state_msg, sizeof(menu->menu_state_msg));
- else
- {
- strlcpy(menu->menu_state_msg,
- msg_hash_to_str(MENU_ENUM_LABEL_VALUE_NO_INFORMATION_AVAILABLE),
- sizeof(menu->menu_state_msg));
- ret = 0;
- }
- }
- }
- BIT64_SET(menu->state, MENU_STATE_RENDER_MESSAGEBOX);
- BIT64_SET(menu->state, MENU_STATE_POST_ITERATE);
- if (action == MENU_ACTION_OK || action == MENU_ACTION_CANCEL)
- {
- BIT64_SET(menu->state, MENU_STATE_POP_STACK);
- }
- break;
- case ITERATE_TYPE_DEFAULT:
- {
- menu_entry_t entry;
- menu_list_t *menu_list = menu_st->entries.list;
- size_t selection = menu_st->selection_ptr;
- size_t menu_list_size = menu_st->entries.list ? MENU_LIST_GET_SELECTION(menu_st->entries.list, 0)->size : 0;
- /* FIXME: Crappy hack, needed for mouse controls
- * to not be completely broken in case we press back.
- *
- * We need to fix this entire mess, mouse controls
- * should not rely on a hack like this in order to work. */
- selection = MAX(MIN(selection, (menu_list_size - 1)), 0);
- MENU_ENTRY_INIT(entry);
- /* NOTE: If menu_entry_action() is modified,
- * will have to verify that these parameters
- * remain unused... */
- entry.rich_label_enabled = false;
- entry.value_enabled = false;
- entry.sublabel_enabled = false;
- menu_entry_get(&entry, 0, selection, NULL, false);
- ret = menu_entry_action(&entry,
- selection, (enum menu_action)action);
- if (ret)
- return -1;
- BIT64_SET(menu->state, MENU_STATE_POST_ITERATE);
- /* Have to defer it so we let settings refresh. */
- if (p_rarch->dialog_st.pending_push)
- {
- const char *label;
- menu_displaylist_info_t info;
- menu_displaylist_info_init(&info);
- info.list = menu_list ? MENU_LIST_GET(menu_list, (unsigned)0) : NULL;
- info.enum_idx = MENU_ENUM_LABEL_HELP;
- /* Set the label string, if it exists. */
- label = msg_hash_to_str(MENU_ENUM_LABEL_HELP);
- if (label)
- info.label = strdup(label);
- menu_displaylist_ctl(DISPLAYLIST_HELP, &info);
- }
- }
- break;
- }
- #ifdef HAVE_ACCESSIBILITY
- if ((last_iterate_type == ITERATE_TYPE_HELP
- || last_iterate_type == ITERATE_TYPE_INFO)
- && last_iterate_type != iterate_type
- && is_accessibility_enabled(p_rarch))
- accessibility_speak_priority(p_rarch,
- "Closed dialog.", 10);
- last_iterate_type = iterate_type;
- #endif
- BIT64_SET(menu->state, MENU_STATE_BLIT);
- if (BIT64_GET(menu->state, MENU_STATE_POP_STACK))
- {
- size_t selection = menu_st->selection_ptr;
- size_t new_selection_ptr = selection;
- menu_entries_pop_stack(&new_selection_ptr, 0, 0);
- menu_st->selection_ptr = selection;
- }
- if (BIT64_GET(menu->state, MENU_STATE_POST_ITERATE))
- {
- menu_input_t *menu_input = &p_rarch->menu_input_state;
- /* If pointer devices are disabled, just ensure mouse
- * cursor is hidden */
- if (menu_input->pointer.type == MENU_POINTER_DISABLED)
- ret = 0;
- else
- ret = menu_input_post_iterate(p_rarch, action,
- current_time);
- menu_input_set_pointer_visibility(
- &p_rarch->menu_input_pointer_hw_state, menu_input, current_time);
- }
- if (ret)
- return -1;
- return 0;
- }
- static bool menu_driver_displaylist_push_internal(
- const char *label,
- menu_displaylist_info_t *info)
- {
- if (string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_HISTORY_TAB)))
- {
- if (menu_displaylist_ctl(DISPLAYLIST_HISTORY, info))
- return true;
- }
- else if (string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_FAVORITES_TAB)))
- {
- if (menu_displaylist_ctl(DISPLAYLIST_FAVORITES, info))
- return true;
- }
- else if (string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_SETTINGS_TAB)))
- {
- if (menu_displaylist_ctl(DISPLAYLIST_SETTINGS_ALL, info))
- return true;
- }
- #ifdef HAVE_CHEATS
- else if (string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_CHEAT_SEARCH_SETTINGS)))
- {
- if (menu_displaylist_ctl(DISPLAYLIST_CHEAT_SEARCH_SETTINGS_LIST, info))
- return true;
- }
- #endif
- else if (string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_MUSIC_TAB)))
- {
- filebrowser_clear_type();
- info->type = 42;
- if (!string_is_empty(info->exts))
- free(info->exts);
- if (!string_is_empty(info->label))
- free(info->label);
- info->exts = strdup("lpl");
- info->label = strdup(
- msg_hash_to_str(MENU_ENUM_LABEL_PLAYLISTS_TAB));
- menu_entries_ctl(MENU_ENTRIES_CTL_CLEAR, info->list);
- menu_displaylist_ctl(DISPLAYLIST_MUSIC_HISTORY, info);
- return true;
- }
- else if (string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_VIDEO_TAB)))
- {
- filebrowser_clear_type();
- info->type = 42;
- if (!string_is_empty(info->exts))
- free(info->exts);
- if (!string_is_empty(info->label))
- free(info->label);
- info->exts = strdup("lpl");
- info->label = strdup(
- msg_hash_to_str(MENU_ENUM_LABEL_PLAYLISTS_TAB));
- menu_entries_ctl(MENU_ENTRIES_CTL_CLEAR, info->list);
- menu_displaylist_ctl(DISPLAYLIST_VIDEO_HISTORY, info);
- return true;
- }
- else if (string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_IMAGES_TAB)))
- {
- filebrowser_clear_type();
- info->type = 42;
- if (!string_is_empty(info->exts))
- free(info->exts);
- if (!string_is_empty(info->label))
- free(info->label);
- info->exts = strdup("lpl");
- info->label = strdup(
- msg_hash_to_str(MENU_ENUM_LABEL_PLAYLISTS_TAB));
- menu_entries_ctl(MENU_ENTRIES_CTL_CLEAR, info->list);
- #if 0
- #ifdef HAVE_SCREENSHOTS
- if (!rarch_ctl(RARCH_CTL_IS_DUMMY_CORE, NULL))
- menu_entries_append_enum(info->list,
- msg_hash_to_str(MENU_ENUM_LABEL_VALUE_TAKE_SCREENSHOT),
- msg_hash_to_str(MENU_ENUM_LABEL_TAKE_SCREENSHOT),
- MENU_ENUM_LABEL_TAKE_SCREENSHOT,
- MENU_SETTING_ACTION_SCREENSHOT, 0, 0);
- else
- info->need_push_no_playlist_entries = true;
- #endif
- #endif
- menu_displaylist_ctl(DISPLAYLIST_IMAGES_HISTORY, info);
- return true;
- }
- else if (string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_PLAYLISTS_TAB)))
- {
- struct rarch_state *p_rarch = &rarch_st;
- settings_t *settings = p_rarch->configuration_settings;
- const char *dir_playlist = settings->paths.directory_playlist;
- filebrowser_clear_type();
- info->type = 42;
- if (!string_is_empty(info->exts))
- free(info->exts);
- if (!string_is_empty(info->label))
- free(info->label);
- info->exts = strdup("lpl");
- info->label = strdup(
- msg_hash_to_str(MENU_ENUM_LABEL_PLAYLISTS_TAB));
- if (string_is_empty(dir_playlist))
- {
- menu_entries_ctl(MENU_ENTRIES_CTL_CLEAR, info->list);
- info->need_refresh = true;
- info->need_push_no_playlist_entries = true;
- info->need_push = true;
- return true;
- }
- if (!string_is_empty(info->path))
- free(info->path);
- info->path = strdup(dir_playlist);
- if (menu_displaylist_ctl(
- DISPLAYLIST_DATABASE_PLAYLISTS, info))
- return true;
- }
- else if (string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_ADD_TAB)))
- {
- if (menu_displaylist_ctl(DISPLAYLIST_SCAN_DIRECTORY_LIST, info))
- return true;
- }
- #if defined(HAVE_LIBRETRODB)
- else if (string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_EXPLORE_TAB)))
- {
- if (menu_displaylist_ctl(DISPLAYLIST_EXPLORE, info))
- return true;
- }
- #endif
- else if (string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_NETPLAY_TAB)))
- {
- if (menu_displaylist_ctl(DISPLAYLIST_NETPLAY_ROOM_LIST, info))
- return true;
- }
- else if (string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_HORIZONTAL_MENU)))
- {
- if (menu_displaylist_ctl(DISPLAYLIST_HORIZONTAL, info))
- return true;
- }
- return false;
- }
- static bool menu_driver_displaylist_push(
- struct rarch_state *p_rarch,
- struct menu_state *menu_st,
- file_list_t *entry_list,
- file_list_t *entry_stack)
- {
- menu_displaylist_info_t info;
- const char *path = NULL;
- const char *label = NULL;
- unsigned type = 0;
- bool ret = false;
- enum msg_hash_enums enum_idx = MSG_UNKNOWN;
- file_list_t *list = MENU_LIST_GET(menu_st->entries.list, 0);
- menu_file_list_cbs_t *cbs = (menu_file_list_cbs_t*)
- list->list[list->size - 1].actiondata;
- menu_displaylist_info_init(&info);
- if (list && list->size)
- file_list_get_at_offset(list, list->size - 1, &path, &label, &type, NULL);
- if (cbs)
- enum_idx = cbs->enum_idx;
- info.list = entry_list;
- info.menu_list = entry_stack;
- info.type = type;
- info.enum_idx = enum_idx;
- if (!string_is_empty(path))
- info.path = strdup(path);
- if (!string_is_empty(label))
- info.label = strdup(label);
- if (!info.list)
- goto error;
- if (menu_driver_displaylist_push_internal(label, &info))
- {
- ret = menu_displaylist_process(&info);
- goto end;
- }
- cbs = (menu_file_list_cbs_t*)list->list[list->size - 1].actiondata;
- if (cbs && cbs->action_deferred_push)
- if (cbs->action_deferred_push(&info) != 0)
- goto error;
- ret = true;
- end:
- menu_displaylist_info_free(&info);
- return ret;
- error:
- menu_displaylist_info_free(&info);
- return false;
- }
- int generic_menu_entry_action(
- void *userdata, menu_entry_t *entry, size_t i, enum menu_action action)
- {
- int ret = 0;
- struct rarch_state *p_rarch = &rarch_st;
- const menu_ctx_driver_t
- *menu_driver_ctx = p_rarch->menu_driver_ctx;
- settings_t *settings = p_rarch->configuration_settings;
- struct menu_state *menu_st = &p_rarch->menu_driver_state;
- menu_list_t *menu_list = menu_st->entries.list;
- file_list_t *selection_buf = menu_list ? MENU_LIST_GET_SELECTION(menu_list, (unsigned)0) : NULL;
- file_list_t *menu_stack = menu_list ? MENU_LIST_GET(menu_list, (unsigned)0) : NULL;
- size_t selection_buf_size = selection_buf ? selection_buf->size : 0;
- menu_file_list_cbs_t *cbs = selection_buf ?
- (menu_file_list_cbs_t*)selection_buf->list[i].actiondata : NULL;
- switch (action)
- {
- case MENU_ACTION_UP:
- if (selection_buf_size > 0)
- {
- size_t scroll_accel = menu_st->scroll.acceleration;
- unsigned scroll_speed = (unsigned)((MAX(scroll_accel, 2) - 2) / 4 + 1);
- bool wraparound_enable = settings->bools.menu_navigation_wraparound_enable;
- if (!(menu_st->selection_ptr == 0 && !wraparound_enable))
- {
- size_t idx = 0;
- if (menu_st->selection_ptr >= scroll_speed)
- idx = menu_st->selection_ptr - scroll_speed;
- else
- {
- idx = selection_buf_size - 1;
- if (!wraparound_enable)
- idx = 0;
- }
- menu_st->selection_ptr = idx;
- menu_driver_navigation_set(true);
- if (menu_driver_ctx->navigation_decrement)
- menu_driver_ctx->navigation_decrement(p_rarch->menu_userdata);
- }
- }
- break;
- case MENU_ACTION_DOWN:
- if (selection_buf_size > 0)
- {
- size_t scroll_accel = menu_st->scroll.acceleration;
- unsigned scroll_speed = (unsigned)((MAX(scroll_accel, 2) - 2) / 4 + 1);
- bool wraparound_enable = settings->bools.menu_navigation_wraparound_enable;
- if (!(menu_st->selection_ptr >= selection_buf_size - 1
- && !wraparound_enable))
- {
- if ((menu_st->selection_ptr + scroll_speed) < selection_buf_size)
- {
- size_t idx = menu_st->selection_ptr + scroll_speed;
- menu_st->selection_ptr = idx;
- menu_driver_navigation_set(true);
- }
- else
- {
- if (wraparound_enable)
- {
- bool pending_push = false;
- menu_driver_ctl(MENU_NAVIGATION_CTL_CLEAR, &pending_push);
- }
- else if (selection_buf_size > 0)
- menu_driver_ctl(MENU_NAVIGATION_CTL_SET_LAST, NULL);
- }
- if (menu_driver_ctx->navigation_increment)
- menu_driver_ctx->navigation_increment(p_rarch->menu_userdata);
- }
- }
- break;
- case MENU_ACTION_SCROLL_UP:
- if (
- menu_st->scroll.index_size
- && menu_st->selection_ptr != 0
- )
- {
- size_t i = menu_st->scroll.index_size - 1;
- while (i
- && menu_st->scroll.index_list[i - 1]
- >= menu_st->selection_ptr)
- i--;
- if (i > 0)
- menu_st->selection_ptr = menu_st->scroll.index_list[i - 1];
- if (menu_driver_ctx->navigation_descend_alphabet)
- menu_driver_ctx->navigation_descend_alphabet(
- p_rarch->menu_userdata, &menu_st->selection_ptr);
- }
- break;
- case MENU_ACTION_SCROLL_DOWN:
- if (menu_st->scroll.index_size)
- {
- if (menu_st->selection_ptr == menu_st->scroll.index_list[menu_st->scroll.index_size - 1])
- menu_st->selection_ptr = selection_buf_size - 1;
- else
- {
- size_t i = 0;
- while (i < menu_st->scroll.index_size - 1
- && menu_st->scroll.index_list[i + 1] <= menu_st->selection_ptr)
- i++;
- menu_st->selection_ptr = menu_st->scroll.index_list[i + 1];
- if (menu_st->selection_ptr >= selection_buf_size)
- menu_st->selection_ptr = selection_buf_size - 1;
- }
- if (menu_driver_ctx->navigation_ascend_alphabet)
- menu_driver_ctx->navigation_ascend_alphabet(
- p_rarch->menu_userdata, &menu_st->selection_ptr);
- }
- break;
- case MENU_ACTION_CANCEL:
- if (cbs && cbs->action_cancel)
- ret = cbs->action_cancel(entry->path,
- entry->label, entry->type, i);
- break;
- case MENU_ACTION_OK:
- if (cbs && cbs->action_ok)
- ret = cbs->action_ok(entry->path,
- entry->label, entry->type, i, entry->entry_idx);
- break;
- case MENU_ACTION_START:
- if (cbs && cbs->action_start)
- ret = cbs->action_start(entry->path,
- entry->label, entry->type, i, entry->entry_idx);
- break;
- case MENU_ACTION_LEFT:
- if (cbs && cbs->action_left)
- ret = cbs->action_left(entry->type, entry->label, false);
- break;
- case MENU_ACTION_RIGHT:
- if (cbs && cbs->action_right)
- ret = cbs->action_right(entry->type, entry->label, false);
- break;
- case MENU_ACTION_INFO:
- if (cbs && cbs->action_info)
- ret = cbs->action_info(entry->type, entry->label);
- break;
- case MENU_ACTION_SELECT:
- if (cbs && cbs->action_select)
- ret = cbs->action_select(entry->path,
- entry->label, entry->type, i, entry->entry_idx);
- break;
- case MENU_ACTION_SEARCH:
- menu_input_dialog_start_search();
- break;
- case MENU_ACTION_SCAN:
- if (cbs && cbs->action_scan)
- ret = cbs->action_scan(entry->path,
- entry->label, entry->type, i);
- break;
- default:
- break;
- }
- if (MENU_ENTRIES_NEEDS_REFRESH(menu_st))
- {
- bool refresh = false;
- menu_driver_displaylist_push(
- p_rarch,
- menu_st,
- selection_buf,
- menu_stack);
- menu_entries_ctl(MENU_ENTRIES_CTL_UNSET_REFRESH, &refresh);
- }
- #ifdef HAVE_ACCESSIBILITY
- if ( action != 0
- && is_accessibility_enabled(p_rarch)
- && !menu_input_dialog_get_display_kb())
- {
- char current_label[255];
- char current_value[255];
- char title_name[255];
- char speak_string[512];
- strlcpy(title_name, "", sizeof(title_name));
- strlcpy(current_label, "", sizeof(current_label));
- get_current_menu_value(&p_rarch->menu_driver_state, current_value, sizeof(current_value));
- switch (action)
- {
- case MENU_ACTION_ACCESSIBILITY_SPEAK_TITLE:
- menu_entries_get_title(title_name, sizeof(title_name));
- break;
- case MENU_ACTION_START:
- /* if equal to '..' we break, else we fall-through */
- if (string_is_equal(current_value, "..."))
- break;
- /* fall-through */
- case MENU_ACTION_ACCESSIBILITY_SPEAK_TITLE_LABEL:
- case MENU_ACTION_OK:
- case MENU_ACTION_LEFT:
- case MENU_ACTION_RIGHT:
- case MENU_ACTION_CANCEL:
- menu_entries_get_title(title_name, sizeof(title_name));
- get_current_menu_label(&p_rarch->menu_driver_state, current_label, sizeof(current_label));
- break;
- case MENU_ACTION_UP:
- case MENU_ACTION_DOWN:
- case MENU_ACTION_SCROLL_UP:
- case MENU_ACTION_SCROLL_DOWN:
- case MENU_ACTION_SELECT:
- case MENU_ACTION_SEARCH:
- case MENU_ACTION_ACCESSIBILITY_SPEAK_LABEL:
- get_current_menu_label(&p_rarch->menu_driver_state, current_label, sizeof(current_label));
- break;
- case MENU_ACTION_SCAN:
- case MENU_ACTION_INFO:
- default:
- break;
- }
- strlcpy(speak_string, "", sizeof(speak_string));
- if (!string_is_equal(title_name, ""))
- {
- strlcpy(speak_string, title_name, sizeof(speak_string));
- strlcat(speak_string, " ", sizeof(speak_string));
- }
- strlcat(speak_string, current_label, sizeof(speak_string));
- if (!string_is_equal(current_value, "..."))
- {
- strlcat(speak_string, " ", sizeof(speak_string));
- strlcat(speak_string, current_value, sizeof(speak_string));
- }
- if (!string_is_equal(speak_string, ""))
- accessibility_speak_priority(p_rarch,
- speak_string, 10);
- }
- #endif
- if (p_rarch->menu_driver_state.pending_close_content)
- {
- menu_handle_t *menu = p_rarch->menu_driver_data;
- const char *content_path = path_get(RARCH_PATH_CONTENT);
- const char *menu_flush_to = msg_hash_to_str(MENU_ENUM_LABEL_MAIN_MENU);
- /* Flush to playlist entry menu if launched via playlist */
- if (menu &&
- !string_is_empty(menu->deferred_path) &&
- !string_is_empty(content_path) &&
- string_is_equal(menu->deferred_path, content_path))
- menu_flush_to = msg_hash_to_str(MENU_ENUM_LABEL_DEFERRED_RPL_ENTRY_ACTIONS);
- command_event(CMD_EVENT_UNLOAD_CORE, NULL);
- menu_entries_flush_stack(menu_flush_to, 0);
- menu_driver_ctl(RARCH_MENU_CTL_UNSET_PREVENT_POPULATE, NULL);
- menu_st->selection_ptr = 0;
- p_rarch->menu_driver_state.pending_close_content = false;
- }
- return ret;
- }
- void menu_navigation_set_selection(size_t val)
- {
- struct rarch_state *p_rarch = &rarch_st;
- struct menu_state *menu_st = &p_rarch->menu_driver_state;
- menu_st->selection_ptr = val;
- }
- void menu_entry_get(menu_entry_t *entry, size_t stack_idx,
- size_t i, void *userdata, bool use_representation)
- {
- char newpath[255];
- const char *path = NULL;
- const char *entry_label = NULL;
- menu_file_list_cbs_t *cbs = NULL;
- struct rarch_state *p_rarch = &rarch_st;
- struct menu_state *menu_st = &p_rarch->menu_driver_state;
- file_list_t *selection_buf = MENU_ENTRIES_GET_SELECTION_BUF_PTR_INTERNAL(menu_st, stack_idx);
- file_list_t *list = (userdata) ? (file_list_t*)userdata : selection_buf;
- bool path_enabled = entry->path_enabled;
- newpath[0] = '\0';
- if (!list)
- return;
- path = list->list[i].path;
- entry_label = list->list[i].label;
- entry->type = list->list[i].type;
- entry->entry_idx = list->list[i].entry_idx;
- cbs = (menu_file_list_cbs_t*)list->list[i].actiondata;
- entry->idx = (unsigned)i;
- if (entry->label_enabled && !string_is_empty(entry_label))
- strlcpy(entry->label, entry_label, sizeof(entry->label));
- if (cbs)
- {
- const char *label = NULL;
- entry->enum_idx = cbs->enum_idx;
- entry->checked = cbs->checked;
- file_list_get_last(MENU_LIST_GET(menu_st->entries.list, 0),
- NULL, &label, NULL, NULL);
- if (entry->rich_label_enabled && cbs->action_label)
- {
- cbs->action_label(list,
- entry->type, (unsigned)i,
- label, path,
- entry->rich_label,
- sizeof(entry->rich_label));
- if (string_is_empty(entry->rich_label))
- path_enabled = true;
- }
- if ((path_enabled || entry->value_enabled) &&
- cbs->action_get_value &&
- use_representation)
- {
- cbs->action_get_value(list,
- &entry->spacing, entry->type,
- (unsigned)i, label,
- entry->value,
- entry->value_enabled ? sizeof(entry->value) : 0,
- path,
- newpath,
- path_enabled ? sizeof(newpath) : 0);
- if (!string_is_empty(entry->value))
- {
- if (entry->enum_idx == MENU_ENUM_LABEL_CHEEVOS_PASSWORD)
- {
- size_t i;
- size_t size = strlcpy(entry->password_value, entry->value,
- sizeof(entry->password_value));
- for (i = 0; i < size; i++)
- entry->password_value[i] = '*';
- }
- }
- }
- if (entry->sublabel_enabled)
- {
- if (!string_is_empty(cbs->action_sublabel_cache))
- strlcpy(entry->sublabel,
- cbs->action_sublabel_cache, sizeof(entry->sublabel));
- else if (cbs->action_sublabel)
- {
- /* If this function callback returns true,
- * we know that the value won't change - so we
- * can cache it instead. */
- if (cbs->action_sublabel(list,
- entry->type, (unsigned)i,
- label, path,
- entry->sublabel,
- sizeof(entry->sublabel)) > 0)
- strlcpy(cbs->action_sublabel_cache,
- entry->sublabel,
- sizeof(cbs->action_sublabel_cache));
- }
- }
- }
- if (path_enabled)
- {
- if (!string_is_empty(path) && !use_representation)
- strlcpy(entry->path, path, sizeof(entry->path));
- else if (
- cbs
- && cbs->setting
- && cbs->setting->enum_value_idx != MSG_UNKNOWN
- && !cbs->setting->dont_use_enum_idx_representation)
- strlcpy(entry->path,
- msg_hash_to_str(cbs->setting->enum_value_idx),
- sizeof(entry->path));
- else
- if (!string_is_empty(newpath))
- strlcpy(entry->path, newpath, sizeof(entry->path));
- }
- }
- int menu_entry_action(
- menu_entry_t *entry, size_t i, enum menu_action action)
- {
- struct rarch_state *p_rarch = &rarch_st;
- if ( p_rarch->menu_driver_ctx
- && p_rarch->menu_driver_ctx->entry_action)
- return p_rarch->menu_driver_ctx->entry_action(
- p_rarch->menu_userdata, entry, i, action);
- return -1;
- }
- static void menu_list_free_list(
- const menu_ctx_driver_t *menu_driver_ctx,
- file_list_t *list)
- {
- unsigned i;
- for (i = 0; i < list->size; i++)
- {
- menu_ctx_list_t list_info;
- list_info.list = list;
- list_info.idx = i;
- list_info.list_size = list->size;
- menu_driver_list_free(menu_driver_ctx, &list_info);
- }
- file_list_free(list);
- }
- static void menu_list_free(
- const menu_ctx_driver_t *menu_driver_ctx,
- menu_list_t *menu_list)
- {
- if (!menu_list)
- return;
- if (menu_list->menu_stack)
- {
- unsigned i;
- for (i = 0; i < menu_list->menu_stack_size; i++)
- {
- if (!menu_list->menu_stack[i])
- continue;
- menu_list_free_list(menu_driver_ctx,
- menu_list->menu_stack[i]);
- menu_list->menu_stack[i] = NULL;
- }
- free(menu_list->menu_stack);
- }
- if (menu_list->selection_buf)
- {
- unsigned i;
- for (i = 0; i < menu_list->selection_buf_size; i++)
- {
- if (!menu_list->selection_buf[i])
- continue;
- menu_list_free_list(menu_driver_ctx,
- menu_list->selection_buf[i]);
- menu_list->selection_buf[i] = NULL;
- }
- free(menu_list->selection_buf);
- }
- free(menu_list);
- }
- static menu_list_t *menu_list_new(const menu_ctx_driver_t *menu_driver_ctx)
- {
- unsigned i;
- menu_list_t *list = (menu_list_t*)malloc(sizeof(*list));
- if (!list)
- return NULL;
- list->menu_stack_size = 1;
- list->selection_buf_size = 1;
- list->selection_buf = NULL;
- list->menu_stack = (file_list_t**)
- calloc(list->menu_stack_size, sizeof(*list->menu_stack));
- if (!list->menu_stack)
- goto error;
- list->selection_buf = (file_list_t**)
- calloc(list->selection_buf_size, sizeof(*list->selection_buf));
- if (!list->selection_buf)
- goto error;
- for (i = 0; i < list->menu_stack_size; i++)
- {
- list->menu_stack[i] = (file_list_t*)
- malloc(sizeof(*list->menu_stack[i]));
- list->menu_stack[i]->list = NULL;
- list->menu_stack[i]->capacity = 0;
- list->menu_stack[i]->size = 0;
- }
- for (i = 0; i < list->selection_buf_size; i++)
- {
- list->selection_buf[i] = (file_list_t*)
- malloc(sizeof(*list->selection_buf[i]));
- list->selection_buf[i]->list = NULL;
- list->selection_buf[i]->capacity = 0;
- list->selection_buf[i]->size = 0;
- }
- return list;
- error:
- menu_list_free(menu_driver_ctx, list);
- return NULL;
- }
- static int menu_list_flush_stack_type(const char *needle, const char *label,
- unsigned type, unsigned final_type)
- {
- return needle ? !string_is_equal(needle, label) : (type != final_type);
- }
- static bool menu_list_pop_stack(
- const menu_ctx_driver_t *menu_driver_ctx,
- void *menu_userdata,
- menu_list_t *list,
- size_t idx,
- size_t *directory_ptr)
- {
- file_list_t *menu_list = MENU_LIST_GET(list, (unsigned)idx);
- if (menu_list->size != 0)
- {
- menu_ctx_list_t list_info;
- list_info.list = menu_list;
- list_info.idx = menu_list->size - 1;
- list_info.list_size = menu_list->size - 1;
- menu_driver_list_free(menu_driver_ctx, &list_info);
- }
- file_list_pop(menu_list, directory_ptr);
- if ( menu_driver_ctx &&
- menu_driver_ctx->list_set_selection)
- menu_driver_ctx->list_set_selection(menu_userdata,
- menu_list);
- return true;
- }
- static void menu_list_flush_stack(
- const menu_ctx_driver_t *menu_driver_ctx,
- void *menu_userdata,
- struct menu_state *menu_st,
- menu_list_t *list,
- size_t idx, const char *needle, unsigned final_type)
- {
- bool refresh = false;
- const char *path = NULL;
- const char *label = NULL;
- unsigned type = 0;
- size_t entry_idx = 0;
- file_list_t *menu_list = MENU_LIST_GET(list, (unsigned)idx);
- menu_entries_ctl(MENU_ENTRIES_CTL_SET_REFRESH, &refresh);
- if (menu_list && menu_list->size)
- file_list_get_at_offset(menu_list, menu_list->size - 1, &path, &label, &type, &entry_idx);
- while (menu_list_flush_stack_type(
- needle, label, type, final_type) != 0)
- {
- bool refresh = false;
- size_t new_selection_ptr = menu_st->selection_ptr;
- bool wont_pop_stack = (MENU_LIST_GET_STACK_SIZE(list, idx) <= 1);
- if (wont_pop_stack)
- break;
- if (menu_driver_ctx->list_cache)
- menu_driver_ctx->list_cache(menu_userdata,
- MENU_LIST_PLAIN, 0);
- menu_list_pop_stack(menu_driver_ctx,
- menu_userdata,
- list, idx, &new_selection_ptr);
- menu_entries_ctl(MENU_ENTRIES_CTL_SET_REFRESH, &refresh);
- menu_st->selection_ptr = new_selection_ptr;
- menu_list = MENU_LIST_GET(list, (unsigned)idx);
- if (menu_list && menu_list->size)
- file_list_get_at_offset(menu_list, menu_list->size - 1, &path, &label, &type, &entry_idx);
- }
- }
- /**
- * menu_entries_elem_get_first_char:
- * @list : File list handle.
- * @offset : Offset index of element.
- *
- * Gets the first character of an element in the
- * file list.
- *
- * Returns: first character of element in file list.
- **/
- static int menu_entries_elem_get_first_char(
- file_list_t *list, unsigned offset)
- {
- const char *path = list->list[offset].alt
- ? list->list[offset].alt
- : list->list[offset].path;
- int ret = path ? TOLOWER((int)*path) : 0;
- /* "Normalize" non-alphabetical entries so they
- * are lumped together for purposes of jumping. */
- if (ret < 'a')
- return ('a' - 1);
- else if (ret > 'z')
- return ('z' + 1);
- return ret;
- }
- static void menu_entries_build_scroll_indices(
- struct menu_state *menu_st,
- file_list_t *list)
- {
- bool current_is_dir = false;
- size_t i = 0;
- int current = menu_entries_elem_get_first_char(list, 0);
- unsigned type = list->list[0].type;
- menu_st->scroll.index_list[0] = 0;
- menu_st->scroll.index_size = 1;
- if (type == FILE_TYPE_DIRECTORY)
- current_is_dir = true;
- for (i = 1; i < list->size; i++)
- {
- int first = menu_entries_elem_get_first_char(list, (unsigned)i);
- bool is_dir = false;
- unsigned idx = (unsigned)i;
- type = list->list[idx].type;
- if (type == FILE_TYPE_DIRECTORY)
- is_dir = true;
- if ((current_is_dir && !is_dir) || (first > current))
- {
- /* Add scroll index */
- menu_st->scroll.index_list[menu_st->scroll.index_size] = i;
- if (!((menu_st->scroll.index_size + 1) >= SCROLL_INDEX_SIZE))
- menu_st->scroll.index_size++;
- }
- current = first;
- current_is_dir = is_dir;
- }
- /* Add scroll index */
- menu_st->scroll.index_list[menu_st->scroll.index_size] = list->size - 1;
- if (!((menu_st->scroll.index_size + 1) >= SCROLL_INDEX_SIZE))
- menu_st->scroll.index_size++;
- }
- menu_file_list_cbs_t *menu_entries_get_last_stack_actiondata(void)
- {
- struct rarch_state *p_rarch = &rarch_st;
- struct menu_state *menu_st = &p_rarch->menu_driver_state;
- if (menu_st->entries.list)
- {
- const file_list_t *list = MENU_LIST_GET(menu_st->entries.list, 0);
- return (menu_file_list_cbs_t*)list->list[list->size - 1].actiondata;
- }
- return NULL;
- }
- /* Sets title to what the name of the current menu should be. */
- int menu_entries_get_title(char *s, size_t len)
- {
- unsigned menu_type = 0;
- const char *path = NULL;
- const char *label = NULL;
- struct rarch_state *p_rarch = &rarch_st;
- struct menu_state *menu_st = &p_rarch->menu_driver_state;
- const file_list_t *list = menu_st->entries.list ?
- MENU_LIST_GET(menu_st->entries.list, 0) : NULL;
- menu_file_list_cbs_t *cbs = list
- ? (menu_file_list_cbs_t*)list->list[list->size - 1].actiondata
- : NULL;
- if (!cbs)
- return -1;
- if (cbs && cbs->action_get_title)
- {
- int ret;
- if (!string_is_empty(cbs->action_title_cache))
- {
- strlcpy(s, cbs->action_title_cache, len);
- return 0;
- }
- file_list_get_last(MENU_LIST_GET(menu_st->entries.list, 0),
- &path, &label, &menu_type, NULL);
- ret = cbs->action_get_title(path, label, menu_type, s, len);
- if (ret == 1)
- strlcpy(cbs->action_title_cache, s, sizeof(cbs->action_title_cache));
- return ret;
- }
- return 0;
- }
- /* Sets 's' to the name of the current core
- * (shown at the top of the UI). */
- int menu_entries_get_core_title(char *s, size_t len)
- {
- struct rarch_state *p_rarch = &rarch_st;
- struct retro_system_info *system = &p_rarch->runloop_system.info;
- const char *core_name = (system && !string_is_empty(system->library_name))
- ? system->library_name
- : msg_hash_to_str(MENU_ENUM_LABEL_VALUE_NO_CORE);
- const char *core_version = (system && system->library_version) ? system->library_version : "";
- #if _MSC_VER == 1200
- strcpy_literal(s, PACKAGE_VERSION " msvc6" " - ");
- #elif _MSC_VER == 1300
- strcpy_literal(s, PACKAGE_VERSION " msvc2002" " - ");
- #elif _MSC_VER == 1310
- strcpy_literal(s, PACKAGE_VERSION " msvc2003" " - ");
- #elif _MSC_VER == 1400
- strcpy_literal(s, PACKAGE_VERSION " msvc2005" " - ");
- #elif _MSC_VER == 1500
- strcpy_literal(s, PACKAGE_VERSION " msvc2008" " - ");
- #elif _MSC_VER == 1600
- strcpy_literal(s, PACKAGE_VERSION " msvc2010" " - ");
- #elif _MSC_VER == 1700
- strcpy_literal(s, PACKAGE_VERSION " msvc2012" " - ");
- #elif _MSC_VER == 1800
- strcpy_literal(s, PACKAGE_VERSION " msvc2013" " - ");
- #elif _MSC_VER == 1900
- strcpy_literal(s, PACKAGE_VERSION " msvc2015" " - ");
- #elif _MSC_VER >= 1910 && _MSC_VER < 1920
- strcpy_literal(s, PACKAGE_VERSION " msvc2017" " - ");
- #elif _MSC_VER >= 1920 && _MSC_VER < 2000
- strcpy_literal(s, PACKAGE_VERSION " msvc2019" " - ");
- #else
- strcpy_literal(s, PACKAGE_VERSION " - ");
- #endif
- strlcat(s, core_name, len);
- if (!string_is_empty(core_version))
- {
- strlcat(s, " (", len);
- strlcat(s, core_version, len);
- strlcat(s, ")", len);
- }
- return 0;
- }
- file_list_t *menu_entries_get_menu_stack_ptr(size_t idx)
- {
- struct rarch_state *p_rarch = &rarch_st;
- struct menu_state *menu_st = &p_rarch->menu_driver_state;
- menu_list_t *menu_list = menu_st->entries.list;
- if (!menu_list)
- return NULL;
- return MENU_LIST_GET(menu_list, (unsigned)idx);
- }
- file_list_t *menu_entries_get_selection_buf_ptr(size_t idx)
- {
- struct rarch_state *p_rarch = &rarch_st;
- struct menu_state *menu_st = &p_rarch->menu_driver_state;
- menu_list_t *menu_list = menu_st->entries.list;
- if (!menu_list)
- return NULL;
- return MENU_LIST_GET_SELECTION(menu_list, (unsigned)idx);
- }
- static void menu_entries_list_deinit(
- const menu_ctx_driver_t *menu_driver_ctx,
- struct menu_state *menu_st)
- {
- if (menu_st->entries.list)
- menu_list_free(menu_driver_ctx, menu_st->entries.list);
- menu_st->entries.list = NULL;
- }
- static void menu_entries_settings_deinit(struct menu_state *menu_st)
- {
- menu_setting_free(menu_st->entries.list_settings);
- if (menu_st->entries.list_settings)
- free(menu_st->entries.list_settings);
- menu_st->entries.list_settings = NULL;
- }
- static bool menu_entries_init(
- struct menu_state *menu_st,
- const menu_ctx_driver_t *menu_driver_ctx
- )
- {
- if (!(menu_st->entries.list = (menu_list_t*)menu_list_new(menu_driver_ctx)))
- return false;
- if (!(menu_st->entries.list_settings = menu_setting_new()))
- return false;
- return true;
- }
- void menu_entries_append(
- file_list_t *list,
- const char *path,
- const char *label,
- unsigned type,
- size_t directory_ptr,
- size_t entry_idx)
- {
- menu_ctx_list_t list_info;
- size_t idx;
- const char *menu_path = NULL;
- menu_file_list_cbs_t *cbs = NULL;
- struct rarch_state *p_rarch = &rarch_st;
- struct menu_state *menu_st = &p_rarch->menu_driver_state;
- if (!list || !label)
- return;
- file_list_append(list, path, label, type, directory_ptr, entry_idx);
- file_list_get_last(MENU_LIST_GET(menu_st->entries.list, 0),
- &menu_path, NULL, NULL, NULL);
- idx = list->size - 1;
- list_info.list = list;
- list_info.path = path;
- list_info.fullpath = NULL;
- if (!string_is_empty(menu_path))
- list_info.fullpath = strdup(menu_path);
- list_info.label = label;
- list_info.idx = idx;
- list_info.entry_type = type;
- if ( p_rarch->menu_driver_ctx &&
- p_rarch->menu_driver_ctx->list_insert)
- p_rarch->menu_driver_ctx->list_insert(
- p_rarch->menu_userdata,
- list_info.list,
- list_info.path,
- list_info.fullpath,
- list_info.label,
- list_info.idx,
- list_info.entry_type);
- if (list_info.fullpath)
- free(list_info.fullpath);
- file_list_free_actiondata(list, idx);
- cbs = (menu_file_list_cbs_t*)
- malloc(sizeof(menu_file_list_cbs_t));
- if (!cbs)
- return;
- cbs->action_sublabel_cache[0] = '\0';
- cbs->action_title_cache[0] = '\0';
- cbs->enum_idx = MSG_UNKNOWN;
- cbs->checked = false;
- cbs->setting = menu_setting_find(label);
- cbs->action_iterate = NULL;
- cbs->action_deferred_push = NULL;
- cbs->action_select = NULL;
- cbs->action_get_title = NULL;
- cbs->action_ok = NULL;
- cbs->action_cancel = NULL;
- cbs->action_scan = NULL;
- cbs->action_start = NULL;
- cbs->action_info = NULL;
- cbs->action_left = NULL;
- cbs->action_right = NULL;
- cbs->action_label = NULL;
- cbs->action_sublabel = NULL;
- cbs->action_get_value = NULL;
- list->list[idx].actiondata = cbs;
- menu_cbs_init(&p_rarch->menu_driver_state,
- p_rarch->menu_driver_ctx,
- list, cbs, path, label, type, idx);
- }
- bool menu_entries_append_enum(
- file_list_t *list,
- const char *path,
- const char *label,
- enum msg_hash_enums enum_idx,
- unsigned type,
- size_t directory_ptr,
- size_t entry_idx)
- {
- menu_ctx_list_t list_info;
- size_t idx;
- const char *menu_path = NULL;
- menu_file_list_cbs_t *cbs = NULL;
- struct rarch_state *p_rarch = &rarch_st;
- struct menu_state *menu_st = &p_rarch->menu_driver_state;
- if (!list || !label)
- return false;
- file_list_append(list, path, label, type, directory_ptr, entry_idx);
- file_list_get_last(MENU_LIST_GET(menu_st->entries.list, 0),
- &menu_path, NULL, NULL, NULL);
- idx = list->size - 1;
- list_info.fullpath = NULL;
- if (!string_is_empty(menu_path))
- list_info.fullpath = strdup(menu_path);
- list_info.list = list;
- list_info.path = path;
- list_info.label = label;
- list_info.idx = idx;
- list_info.entry_type = type;
- if ( p_rarch->menu_driver_ctx &&
- p_rarch->menu_driver_ctx->list_insert)
- p_rarch->menu_driver_ctx->list_insert(
- p_rarch->menu_userdata,
- list_info.list,
- list_info.path,
- list_info.fullpath,
- list_info.label,
- list_info.idx,
- list_info.entry_type);
- if (list_info.fullpath)
- free(list_info.fullpath);
- file_list_free_actiondata(list, idx);
- cbs = (menu_file_list_cbs_t*)
- malloc(sizeof(menu_file_list_cbs_t));
- if (!cbs)
- return false;
- cbs->action_sublabel_cache[0] = '\0';
- cbs->action_title_cache[0] = '\0';
- cbs->enum_idx = enum_idx;
- cbs->checked = false;
- cbs->setting = NULL;
- cbs->action_iterate = NULL;
- cbs->action_deferred_push = NULL;
- cbs->action_select = NULL;
- cbs->action_get_title = NULL;
- cbs->action_ok = NULL;
- cbs->action_cancel = NULL;
- cbs->action_scan = NULL;
- cbs->action_start = NULL;
- cbs->action_info = NULL;
- cbs->action_left = NULL;
- cbs->action_right = NULL;
- cbs->action_label = NULL;
- cbs->action_sublabel = NULL;
- cbs->action_get_value = NULL;
- list->list[idx].actiondata = cbs;
- if ( enum_idx != MENU_ENUM_LABEL_PLAYLIST_ENTRY
- && enum_idx != MENU_ENUM_LABEL_PLAYLIST_COLLECTION_ENTRY
- && enum_idx != MENU_ENUM_LABEL_EXPLORE_ITEM
- && enum_idx != MENU_ENUM_LABEL_RDB_ENTRY)
- cbs->setting = menu_setting_find_enum(enum_idx);
- menu_cbs_init(&p_rarch->menu_driver_state,
- p_rarch->menu_driver_ctx,
- list, cbs, path, label, type, idx);
- return true;
- }
- void menu_entries_prepend(file_list_t *list,
- const char *path, const char *label,
- enum msg_hash_enums enum_idx,
- unsigned type, size_t directory_ptr, size_t entry_idx)
- {
- menu_ctx_list_t list_info;
- size_t idx = 0;
- const char *menu_path = NULL;
- menu_file_list_cbs_t *cbs = NULL;
- struct rarch_state *p_rarch = &rarch_st;
- struct menu_state *menu_st = &p_rarch->menu_driver_state;
- if (!list || !label)
- return;
- file_list_prepend(list, path, label, type, directory_ptr, entry_idx);
- file_list_get_last(MENU_LIST_GET(menu_st->entries.list, 0),
- &menu_path, NULL, NULL, NULL);
- list_info.fullpath = NULL;
- if (!string_is_empty(menu_path))
- list_info.fullpath = strdup(menu_path);
- list_info.list = list;
- list_info.path = path;
- list_info.label = label;
- list_info.idx = idx;
- list_info.entry_type = type;
- if ( p_rarch->menu_driver_ctx &&
- p_rarch->menu_driver_ctx->list_insert)
- p_rarch->menu_driver_ctx->list_insert(
- p_rarch->menu_userdata,
- list_info.list,
- list_info.path,
- list_info.fullpath,
- list_info.label,
- list_info.idx,
- list_info.entry_type);
- if (list_info.fullpath)
- free(list_info.fullpath);
- file_list_free_actiondata(list, idx);
- cbs = (menu_file_list_cbs_t*)
- malloc(sizeof(menu_file_list_cbs_t));
- if (!cbs)
- return;
- cbs->action_sublabel_cache[0] = '\0';
- cbs->action_title_cache[0] = '\0';
- cbs->enum_idx = enum_idx;
- cbs->checked = false;
- cbs->setting = menu_setting_find_enum(cbs->enum_idx);
- cbs->action_iterate = NULL;
- cbs->action_deferred_push = NULL;
- cbs->action_select = NULL;
- cbs->action_get_title = NULL;
- cbs->action_ok = NULL;
- cbs->action_cancel = NULL;
- cbs->action_scan = NULL;
- cbs->action_start = NULL;
- cbs->action_info = NULL;
- cbs->action_left = NULL;
- cbs->action_right = NULL;
- cbs->action_label = NULL;
- cbs->action_sublabel = NULL;
- cbs->action_get_value = NULL;
- list->list[idx].actiondata = cbs;
- menu_cbs_init(&p_rarch->menu_driver_state,
- p_rarch->menu_driver_ctx,
- list, cbs, path, label, type, idx);
- }
- void menu_entries_get_last_stack(const char **path, const char **label,
- unsigned *file_type, enum msg_hash_enums *enum_idx, size_t *entry_idx)
- {
- file_list_t *list = NULL;
- struct rarch_state *p_rarch = &rarch_st;
- struct menu_state *menu_st = &p_rarch->menu_driver_state;
- if (!menu_st->entries.list)
- return;
- list = MENU_LIST_GET(menu_st->entries.list, 0);
- if (list && list->size)
- file_list_get_at_offset(list, list->size - 1, path, label, file_type, entry_idx);
- if (enum_idx)
- {
- menu_file_list_cbs_t *cbs = (menu_file_list_cbs_t*)
- list->list[list->size - 1].actiondata;
- if (cbs)
- *enum_idx = cbs->enum_idx;
- }
- }
- void menu_entries_flush_stack(const char *needle, unsigned final_type)
- {
- struct rarch_state *p_rarch = &rarch_st;
- struct menu_state *menu_st = &p_rarch->menu_driver_state;
- menu_list_t *menu_list = menu_st->entries.list;
- if (menu_list)
- menu_list_flush_stack(
- p_rarch->menu_driver_ctx,
- p_rarch->menu_userdata,
- menu_st,
- menu_list, 0, needle, final_type);
- }
- void menu_entries_pop_stack(size_t *ptr, size_t idx, bool animate)
- {
- struct rarch_state *p_rarch = &rarch_st;
- const menu_ctx_driver_t *menu_driver_ctx = p_rarch->menu_driver_ctx;
- struct menu_state *menu_st = &p_rarch->menu_driver_state;
- menu_list_t *menu_list = menu_st->entries.list;
- if (!menu_list)
- return;
- if (MENU_LIST_GET_STACK_SIZE(menu_list, idx) > 1)
- {
- bool refresh = false;
- if (animate)
- {
- if (menu_driver_ctx->list_cache)
- menu_driver_ctx->list_cache(p_rarch->menu_userdata,
- MENU_LIST_PLAIN, 0);
- }
- menu_list_pop_stack(menu_driver_ctx,
- p_rarch->menu_userdata, menu_list, idx, ptr);
- if (animate)
- menu_entries_ctl(MENU_ENTRIES_CTL_SET_REFRESH, &refresh);
- }
- }
- size_t menu_entries_get_stack_size(size_t idx)
- {
- struct rarch_state *p_rarch = &rarch_st;
- struct menu_state *menu_st = &p_rarch->menu_driver_state;
- menu_list_t *menu_list = menu_st->entries.list;
- if (!menu_list)
- return 0;
- return MENU_LIST_GET_STACK_SIZE(menu_list, idx);
- }
- size_t menu_entries_get_size(void)
- {
- struct rarch_state *p_rarch = &rarch_st;
- struct menu_state *menu_st = &p_rarch->menu_driver_state;
- menu_list_t *menu_list = menu_st->entries.list;
- if (!menu_list)
- return 0;
- return MENU_LIST_GET_SELECTION(menu_list, 0)->size;
- }
- bool menu_entries_ctl(enum menu_entries_ctl_state state, void *data)
- {
- struct rarch_state *p_rarch = &rarch_st;
- struct menu_state *menu_st = &p_rarch->menu_driver_state;
- switch (state)
- {
- case MENU_ENTRIES_CTL_NEEDS_REFRESH:
- return MENU_ENTRIES_NEEDS_REFRESH(menu_st);
- case MENU_ENTRIES_CTL_SETTINGS_GET:
- {
- rarch_setting_t **settings = (rarch_setting_t**)data;
- if (!settings)
- return false;
- *settings = menu_st->entries.list_settings;
- }
- break;
- case MENU_ENTRIES_CTL_SET_REFRESH:
- {
- bool *nonblocking = (bool*)data;
- if (*nonblocking)
- menu_st->entries_nonblocking_refresh = true;
- else
- menu_st->entries_need_refresh = true;
- }
- break;
- case MENU_ENTRIES_CTL_UNSET_REFRESH:
- {
- bool *nonblocking = (bool*)data;
- if (*nonblocking)
- menu_st->entries_nonblocking_refresh = false;
- else
- menu_st->entries_need_refresh = false;
- }
- break;
- case MENU_ENTRIES_CTL_SET_START:
- {
- size_t *idx = (size_t*)data;
- if (idx)
- menu_st->entries.begin = *idx;
- }
- case MENU_ENTRIES_CTL_START_GET:
- {
- size_t *idx = (size_t*)data;
- if (!idx)
- return 0;
- *idx = menu_st->entries.begin;
- }
- break;
- case MENU_ENTRIES_CTL_REFRESH:
- /**
- * Before a refresh, we could have deleted a
- * file on disk, causing selection_ptr to
- * suddendly be out of range.
- *
- * Ensure it doesn't overflow.
- **/
- {
- size_t list_size;
- file_list_t *list = (file_list_t*)data;
- if (!list)
- return false;
- if (list->size)
- menu_entries_build_scroll_indices(menu_st, list);
- list_size = menu_st->entries.list ? MENU_LIST_GET_SELECTION(menu_st->entries.list, 0)->size : 0;
- if (list_size)
- {
- size_t selection = menu_st->selection_ptr;
- if (selection >= list_size)
- {
- size_t idx = list_size - 1;
- menu_st->selection_ptr = idx;
- menu_driver_navigation_set(true);
- }
- }
- else
- {
- bool pending_push = true;
- menu_driver_ctl(MENU_NAVIGATION_CTL_CLEAR, &pending_push);
- }
- }
- break;
- case MENU_ENTRIES_CTL_CLEAR:
- {
- unsigned i;
- file_list_t *list = (file_list_t*)data;
- if (!list)
- return false;
- /* Clear all the menu lists. */
- if (p_rarch->menu_driver_ctx->list_clear)
- p_rarch->menu_driver_ctx->list_clear(list);
- for (i = 0; i < list->size; i++)
- {
- if (list->list[i].actiondata)
- free(list->list[i].actiondata);
- list->list[i].actiondata = NULL;
- }
- file_list_clear(list);
- }
- break;
- case MENU_ENTRIES_CTL_SHOW_BACK:
- /* Returns true if a Back button should be shown
- * (i.e. we are at least
- * one level deep in the menu hierarchy). */
- if (!menu_st->entries.list)
- return false;
- return (MENU_LIST_GET_STACK_SIZE(menu_st->entries.list, 0) > 1);
- case MENU_ENTRIES_CTL_NONE:
- default:
- break;
- }
- return true;
- }
- static void menu_display_common_image_upload(
- const menu_ctx_driver_t *menu_driver_ctx,
- void *menu_userdata,
- struct texture_image *img,
- void *user_data,
- unsigned type)
- {
- if ( menu_driver_ctx
- && menu_driver_ctx->load_image)
- menu_driver_ctx->load_image(menu_userdata,
- img, (enum menu_image_type)type);
- image_texture_free(img);
- free(img);
- free(user_data);
- }
- /* TODO/FIXME - seems only RGUI uses this - can this be
- * refactored away or we can have one common function used
- * across all menu drivers? */
- #ifdef HAVE_RGUI
- void menu_display_handle_thumbnail_upload(
- retro_task_t *task,
- void *task_data,
- void *user_data, const char *err)
- {
- struct rarch_state *p_rarch = &rarch_st;
- menu_display_common_image_upload(
- p_rarch->menu_driver_ctx,
- p_rarch->menu_userdata,
- (struct texture_image*)task_data,
- user_data,
- MENU_IMAGE_THUMBNAIL);
- }
- void menu_display_handle_left_thumbnail_upload(
- retro_task_t *task,
- void *task_data,
- void *user_data, const char *err)
- {
- struct rarch_state *p_rarch = &rarch_st;
- menu_display_common_image_upload(
- p_rarch->menu_driver_ctx,
- p_rarch->menu_userdata,
- (struct texture_image*)task_data,
- user_data,
- MENU_IMAGE_LEFT_THUMBNAIL);
- }
- #endif
- void menu_display_handle_savestate_thumbnail_upload(
- retro_task_t *task,
- void *task_data,
- void *user_data, const char *err)
- {
- struct rarch_state *p_rarch = &rarch_st;
- menu_display_common_image_upload(
- p_rarch->menu_driver_ctx,
- p_rarch->menu_userdata,
- (struct texture_image*)task_data,
- user_data,
- MENU_IMAGE_SAVESTATE_THUMBNAIL);
- }
- /* Function that gets called when we want to load in a
- * new menu wallpaper.
- */
- void menu_display_handle_wallpaper_upload(
- retro_task_t *task,
- void *task_data,
- void *user_data, const char *err)
- {
- struct rarch_state *p_rarch = &rarch_st;
- menu_display_common_image_upload(
- p_rarch->menu_driver_ctx,
- p_rarch->menu_userdata,
- (struct texture_image*)task_data,
- user_data,
- MENU_IMAGE_WALLPAPER);
- }
- /**
- * config_get_menu_driver_options:
- *
- * Get an enumerated list of all menu driver names,
- * separated by '|'.
- *
- * Returns: string listing of all menu driver names,
- * separated by '|'.
- **/
- const char *config_get_menu_driver_options(void)
- {
- return char_list_new_special(STRING_LIST_MENU_DRIVERS, NULL);
- }
- #ifdef HAVE_COMPRESSION
- /* This function gets called at first startup on Android/iOS
- * when we need to extract the APK contents/zip file. This
- * file contains assets which then get extracted to the
- * user's asset directories. */
- static void bundle_decompressed(retro_task_t *task,
- void *task_data,
- void *user_data, const char *err)
- {
- struct rarch_state *p_rarch = &rarch_st;
- settings_t *settings = p_rarch->configuration_settings;
- decompress_task_data_t *dec = (decompress_task_data_t*)task_data;
- if (err)
- RARCH_ERR("%s", err);
- if (dec)
- {
- if (!err)
- command_event(CMD_EVENT_REINIT, NULL);
- /* delete bundle? */
- free(dec->source_file);
- free(dec);
- }
- configuration_set_uint(settings,
- settings->uints.bundle_assets_extract_last_version,
- settings->uints.bundle_assets_extract_version_current);
- configuration_set_bool(settings, settings->bools.bundle_finished, true);
- command_event(CMD_EVENT_MENU_SAVE_CURRENT_CONFIG, NULL);
- }
- #endif
- /**
- * menu_init:
- * @data : Menu context handle.
- *
- * Create and initialize menu handle.
- *
- * Returns: menu handle on success, otherwise NULL.
- **/
- static bool menu_init(
- struct menu_state *menu_st,
- menu_dialog_t *p_dialog,
- const menu_ctx_driver_t *menu_driver_ctx,
- menu_input_t *menu_input,
- menu_input_pointer_hw_state_t *pointer_hw_state,
- settings_t *settings
- )
- {
- #ifdef HAVE_CONFIGFILE
- bool menu_show_start_screen = settings->bools.menu_show_start_screen;
- bool config_save_on_exit = settings->bools.config_save_on_exit;
- #endif
- /* Ensure that menu pointer input is correctly
- * initialised */
- menu_input_reset(menu_input, pointer_hw_state);
- if (!menu_entries_init(menu_st, menu_driver_ctx))
- {
- menu_entries_settings_deinit(menu_st);
- menu_entries_list_deinit(menu_driver_ctx, menu_st);
- return false;
- }
- #ifdef HAVE_CONFIGFILE
- if (menu_show_start_screen)
- {
- /* We don't want the welcome dialog screen to show up
- * again after the first startup, so we save to config
- * file immediately. */
- p_dialog->current_type = MENU_DIALOG_WELCOME;
- configuration_set_bool(settings,
- settings->bools.menu_show_start_screen, false);
- if (config_save_on_exit)
- command_event(CMD_EVENT_MENU_SAVE_CURRENT_CONFIG, NULL);
- }
- #endif
- #ifdef HAVE_COMPRESSION
- if ( settings->bools.bundle_assets_extract_enable
- && !string_is_empty(settings->arrays.bundle_assets_src)
- && !string_is_empty(settings->arrays.bundle_assets_dst)
- && (settings->uints.bundle_assets_extract_version_current
- != settings->uints.bundle_assets_extract_last_version)
- )
- {
- p_dialog->current_type = MENU_DIALOG_HELP_EXTRACT;
- task_push_decompress(
- settings->arrays.bundle_assets_src,
- settings->arrays.bundle_assets_dst,
- NULL,
- settings->arrays.bundle_assets_dst_subdir,
- NULL,
- bundle_decompressed,
- NULL,
- NULL,
- false);
- /* Support only 1 version - setting this would prevent the assets from being extracted every time */
- configuration_set_int(settings,
- settings->uints.bundle_assets_extract_last_version, 1);
- }
- #endif
- #if defined(HAVE_CG) || defined(HAVE_GLSL) || defined(HAVE_SLANG) || defined(HAVE_HLSL)
- menu_shader_manager_init();
- #endif
- return true;
- }
- const char *menu_driver_ident(void)
- {
- struct rarch_state *p_rarch = &rarch_st;
- if (p_rarch->menu_driver_alive)
- if (p_rarch->menu_driver_ctx && p_rarch->menu_driver_ctx->ident)
- return p_rarch->menu_driver_ctx->ident;
- return NULL;
- }
- void menu_driver_frame(bool menu_is_alive, video_frame_info_t *video_info)
- {
- struct rarch_state *p_rarch = &rarch_st;
- if (menu_is_alive && p_rarch->menu_driver_ctx->frame)
- p_rarch->menu_driver_ctx->frame(p_rarch->menu_userdata, video_info);
- }
- /* Time format strings with AM-PM designation require special
- * handling due to platform dependence */
- static void strftime_am_pm(char* ptr, size_t maxsize, const char* format,
- const struct tm* timeptr)
- {
- char *local = NULL;
- /* Ensure correct locale is set
- * > Required for localised AM/PM strings */
- setlocale(LC_TIME, "");
- strftime(ptr, maxsize, format, timeptr);
- #if !(defined(__linux__) && !defined(ANDROID))
- local = local_to_utf8_string_alloc(ptr);
- if (!string_is_empty(local))
- strlcpy(ptr, local, maxsize);
- if (local)
- {
- free(local);
- local = NULL;
- }
- #endif
- }
- /* Display the date and time - time_mode will influence how
- * the time representation will look like.
- * */
- void menu_display_timedate(gfx_display_ctx_datetime_t *datetime)
- {
- struct rarch_state *p_rarch = &rarch_st;
- struct menu_state *menu_st = &p_rarch->menu_driver_state;
- if (!datetime)
- return;
- /* Trigger an update, if required */
- if (menu_st->current_time_us - menu_st->datetime_last_time_us >=
- DATETIME_CHECK_INTERVAL)
- {
- time_t time_;
- struct tm tm_;
- bool has_am_pm = false;
- const char *format_str = "";
- menu_st->datetime_last_time_us = menu_st->current_time_us;
- /* Get current time */
- time(&time_);
- rtime_localtime(&time_, &tm_);
- /* Format string representation */
- switch (datetime->time_mode)
- {
- case MENU_TIMEDATE_STYLE_YMD_HMS: /* YYYY-MM-DD HH:MM:SS */
- /* Using switch statements to set the format
- * string is verbose, but has far less performance
- * impact than setting the date separator dynamically
- * (i.e. no snprintf() or character replacement...) */
- switch (datetime->date_separator)
- {
- case MENU_TIMEDATE_DATE_SEPARATOR_SLASH:
- format_str = "%Y/%m/%d %H:%M:%S";
- break;
- case MENU_TIMEDATE_DATE_SEPARATOR_PERIOD:
- format_str = "%Y.%m.%d %H:%M:%S";
- break;
- default:
- format_str = "%Y-%m-%d %H:%M:%S";
- break;
- }
- break;
- case MENU_TIMEDATE_STYLE_YMD_HM: /* YYYY-MM-DD HH:MM */
- switch (datetime->date_separator)
- {
- case MENU_TIMEDATE_DATE_SEPARATOR_SLASH:
- format_str = "%Y/%m/%d %H:%M";
- break;
- case MENU_TIMEDATE_DATE_SEPARATOR_PERIOD:
- format_str = "%Y.%m.%d %H:%M";
- break;
- default:
- format_str = "%Y-%m-%d %H:%M";
- break;
- }
- break;
- case MENU_TIMEDATE_STYLE_YMD: /* YYYY-MM-DD */
- switch (datetime->date_separator)
- {
- case MENU_TIMEDATE_DATE_SEPARATOR_SLASH:
- format_str = "%Y/%m/%d";
- break;
- case MENU_TIMEDATE_DATE_SEPARATOR_PERIOD:
- format_str = "%Y.%m.%d";
- break;
- default:
- format_str = "%Y-%m-%d";
- break;
- }
- break;
- case MENU_TIMEDATE_STYLE_YM: /* YYYY-MM */
- switch (datetime->date_separator)
- {
- case MENU_TIMEDATE_DATE_SEPARATOR_SLASH:
- format_str = "%Y/%m";
- break;
- case MENU_TIMEDATE_DATE_SEPARATOR_PERIOD:
- format_str = "%Y.%m";
- break;
- default:
- format_str = "%Y-%m";
- break;
- }
- break;
- case MENU_TIMEDATE_STYLE_MDYYYY_HMS: /* MM-DD-YYYY HH:MM:SS */
- switch (datetime->date_separator)
- {
- case MENU_TIMEDATE_DATE_SEPARATOR_SLASH:
- format_str = "%m/%d/%Y %H:%M:%S";
- break;
- case MENU_TIMEDATE_DATE_SEPARATOR_PERIOD:
- format_str = "%m.%d.%Y %H:%M:%S";
- break;
- default:
- format_str = "%m-%d-%Y %H:%M:%S";
- break;
- }
- break;
- case MENU_TIMEDATE_STYLE_MDYYYY_HM: /* MM-DD-YYYY HH:MM */
- switch (datetime->date_separator)
- {
- case MENU_TIMEDATE_DATE_SEPARATOR_SLASH:
- format_str = "%m/%d/%Y %H:%M";
- break;
- case MENU_TIMEDATE_DATE_SEPARATOR_PERIOD:
- format_str = "%m.%d.%Y %H:%M";
- break;
- default:
- format_str = "%m-%d-%Y %H:%M";
- break;
- }
- break;
- case MENU_TIMEDATE_STYLE_MD_HM: /* MM-DD HH:MM */
- switch (datetime->date_separator)
- {
- case MENU_TIMEDATE_DATE_SEPARATOR_SLASH:
- format_str = "%m/%d %H:%M";
- break;
- case MENU_TIMEDATE_DATE_SEPARATOR_PERIOD:
- format_str = "%m.%d %H:%M";
- break;
- default:
- format_str = "%m-%d %H:%M";
- break;
- }
- break;
- case MENU_TIMEDATE_STYLE_MDYYYY: /* MM-DD-YYYY */
- switch (datetime->date_separator)
- {
- case MENU_TIMEDATE_DATE_SEPARATOR_SLASH:
- format_str = "%m/%d/%Y";
- break;
- case MENU_TIMEDATE_DATE_SEPARATOR_PERIOD:
- format_str = "%m.%d.%Y";
- break;
- default:
- format_str = "%m-%d-%Y";
- break;
- }
- break;
- case MENU_TIMEDATE_STYLE_MD: /* MM-DD */
- switch (datetime->date_separator)
- {
- case MENU_TIMEDATE_DATE_SEPARATOR_SLASH:
- format_str = "%m/%d";
- break;
- case MENU_TIMEDATE_DATE_SEPARATOR_PERIOD:
- format_str = "%m.%d";
- break;
- default:
- format_str = "%m-%d";
- break;
- }
- break;
- case MENU_TIMEDATE_STYLE_DDMMYYYY_HMS: /* DD-MM-YYYY HH:MM:SS */
- switch (datetime->date_separator)
- {
- case MENU_TIMEDATE_DATE_SEPARATOR_SLASH:
- format_str = "%d/%m/%Y %H:%M:%S";
- break;
- case MENU_TIMEDATE_DATE_SEPARATOR_PERIOD:
- format_str = "%d.%m.%Y %H:%M:%S";
- break;
- default:
- format_str = "%d-%m-%Y %H:%M:%S";
- break;
- }
- break;
- case MENU_TIMEDATE_STYLE_DDMMYYYY_HM: /* DD-MM-YYYY HH:MM */
- switch (datetime->date_separator)
- {
- case MENU_TIMEDATE_DATE_SEPARATOR_SLASH:
- format_str = "%d/%m/%Y %H:%M";
- break;
- case MENU_TIMEDATE_DATE_SEPARATOR_PERIOD:
- format_str = "%d.%m.%Y %H:%M";
- break;
- default:
- format_str = "%d-%m-%Y %H:%M";
- break;
- }
- break;
- case MENU_TIMEDATE_STYLE_DDMM_HM: /* DD-MM HH:MM */
- switch (datetime->date_separator)
- {
- case MENU_TIMEDATE_DATE_SEPARATOR_SLASH:
- format_str = "%d/%m %H:%M";
- break;
- case MENU_TIMEDATE_DATE_SEPARATOR_PERIOD:
- format_str = "%d.%m %H:%M";
- break;
- default:
- format_str = "%d-%m %H:%M";
- break;
- }
- break;
- case MENU_TIMEDATE_STYLE_DDMMYYYY: /* DD-MM-YYYY */
- switch (datetime->date_separator)
- {
- case MENU_TIMEDATE_DATE_SEPARATOR_SLASH:
- format_str = "%d/%m/%Y";
- break;
- case MENU_TIMEDATE_DATE_SEPARATOR_PERIOD:
- format_str = "%d.%m.%Y";
- break;
- default:
- format_str = "%d-%m-%Y";
- break;
- }
- break;
- case MENU_TIMEDATE_STYLE_DDMM: /* DD-MM */
- switch (datetime->date_separator)
- {
- case MENU_TIMEDATE_DATE_SEPARATOR_SLASH:
- format_str = "%d/%m";
- break;
- case MENU_TIMEDATE_DATE_SEPARATOR_PERIOD:
- format_str = "%d.%m";
- break;
- default:
- format_str = "%d-%m";
- break;
- }
- break;
- case MENU_TIMEDATE_STYLE_HMS: /* HH:MM:SS */
- format_str = "%H:%M:%S";
- break;
- case MENU_TIMEDATE_STYLE_HM: /* HH:MM */
- format_str = "%H:%M";
- break;
- case MENU_TIMEDATE_STYLE_YMD_HMS_AMPM: /* YYYY-MM-DD HH:MM:SS (AM/PM) */
- has_am_pm = true;
- switch (datetime->date_separator)
- {
- case MENU_TIMEDATE_DATE_SEPARATOR_SLASH:
- format_str = "%Y/%m/%d %I:%M:%S %p";
- break;
- case MENU_TIMEDATE_DATE_SEPARATOR_PERIOD:
- format_str = "%Y.%m.%d %I:%M:%S %p";
- break;
- default:
- format_str = "%Y-%m-%d %I:%M:%S %p";
- break;
- }
- break;
- case MENU_TIMEDATE_STYLE_YMD_HM_AMPM: /* YYYY-MM-DD HH:MM (AM/PM) */
- has_am_pm = true;
- switch (datetime->date_separator)
- {
- case MENU_TIMEDATE_DATE_SEPARATOR_SLASH:
- format_str = "%Y/%m/%d %I:%M %p";
- break;
- case MENU_TIMEDATE_DATE_SEPARATOR_PERIOD:
- format_str = "%Y.%m.%d %I:%M %p";
- break;
- default:
- format_str = "%Y-%m-%d %I:%M %p";
- break;
- }
- break;
- case MENU_TIMEDATE_STYLE_MDYYYY_HMS_AMPM: /* MM-DD-YYYY HH:MM:SS (AM/PM) */
- has_am_pm = true;
- switch (datetime->date_separator)
- {
- case MENU_TIMEDATE_DATE_SEPARATOR_SLASH:
- format_str = "%m/%d/%Y %I:%M:%S %p";
- break;
- case MENU_TIMEDATE_DATE_SEPARATOR_PERIOD:
- format_str = "%m.%d.%Y %I:%M:%S %p";
- break;
- default:
- format_str = "%m-%d-%Y %I:%M:%S %p";
- break;
- }
- break;
- case MENU_TIMEDATE_STYLE_MDYYYY_HM_AMPM: /* MM-DD-YYYY HH:MM (AM/PM) */
- has_am_pm = true;
- switch (datetime->date_separator)
- {
- case MENU_TIMEDATE_DATE_SEPARATOR_SLASH:
- format_str = "%m/%d/%Y %I:%M %p";
- break;
- case MENU_TIMEDATE_DATE_SEPARATOR_PERIOD:
- format_str = "%m.%d.%Y %I:%M %p";
- break;
- default:
- format_str = "%m-%d-%Y %I:%M %p";
- break;
- }
- break;
- case MENU_TIMEDATE_STYLE_MD_HM_AMPM: /* MM-DD HH:MM (AM/PM) */
- has_am_pm = true;
- switch (datetime->date_separator)
- {
- case MENU_TIMEDATE_DATE_SEPARATOR_SLASH:
- format_str = "%m/%d %I:%M %p";
- break;
- case MENU_TIMEDATE_DATE_SEPARATOR_PERIOD:
- format_str = "%m.%d %I:%M %p";
- break;
- default:
- format_str = "%m-%d %I:%M %p";
- break;
- }
- break;
- case MENU_TIMEDATE_STYLE_DDMMYYYY_HMS_AMPM: /* DD-MM-YYYY HH:MM:SS (AM/PM) */
- has_am_pm = true;
- switch (datetime->date_separator)
- {
- case MENU_TIMEDATE_DATE_SEPARATOR_SLASH:
- format_str = "%d/%m/%Y %I:%M:%S %p";
- break;
- case MENU_TIMEDATE_DATE_SEPARATOR_PERIOD:
- format_str = "%d.%m.%Y %I:%M:%S %p";
- break;
- default:
- format_str = "%d-%m-%Y %I:%M:%S %p";
- break;
- }
- break;
- case MENU_TIMEDATE_STYLE_DDMMYYYY_HM_AMPM: /* DD-MM-YYYY HH:MM (AM/PM) */
- has_am_pm = true;
- switch (datetime->date_separator)
- {
- case MENU_TIMEDATE_DATE_SEPARATOR_SLASH:
- format_str = "%d/%m/%Y %I:%M %p";
- break;
- case MENU_TIMEDATE_DATE_SEPARATOR_PERIOD:
- format_str = "%d.%m.%Y %I:%M %p";
- break;
- default:
- format_str = "%d-%m-%Y %I:%M %p";
- break;
- }
- break;
- case MENU_TIMEDATE_STYLE_DDMM_HM_AMPM: /* DD-MM HH:MM (AM/PM) */
- has_am_pm = true;
- switch (datetime->date_separator)
- {
- case MENU_TIMEDATE_DATE_SEPARATOR_SLASH:
- format_str = "%d/%m %I:%M %p";
- break;
- case MENU_TIMEDATE_DATE_SEPARATOR_PERIOD:
- format_str = "%d.%m %I:%M %p";
- break;
- default:
- format_str = "%d-%m %I:%M %p";
- break;
- }
- break;
- case MENU_TIMEDATE_STYLE_HMS_AMPM: /* HH:MM:SS (AM/PM) */
- has_am_pm = true;
- format_str = "%I:%M:%S %p";
- break;
- case MENU_TIMEDATE_STYLE_HM_AMPM: /* HH:MM (AM/PM) */
- has_am_pm = true;
- format_str = "%I:%M %p";
- break;
- }
- if (has_am_pm)
- strftime_am_pm(menu_st->datetime_cache, sizeof(menu_st->datetime_cache),
- format_str, &tm_);
- else
- strftime(menu_st->datetime_cache, sizeof(menu_st->datetime_cache),
- format_str, &tm_);
- }
- /* Copy cached datetime string to input
- * menu_display_ctx_datetime_t struct */
- strlcpy(datetime->s, menu_st->datetime_cache, datetime->len);
- }
- /* Display current (battery) power state */
- void menu_display_powerstate(gfx_display_ctx_powerstate_t *powerstate)
- {
- int percent = 0;
- struct rarch_state *p_rarch = &rarch_st;
- struct menu_state *menu_st = &p_rarch->menu_driver_state;
- enum frontend_powerstate state = FRONTEND_POWERSTATE_NONE;
- if (!powerstate)
- return;
- /* Trigger an update, if required */
- if (menu_st->current_time_us - menu_st->powerstate_last_time_us >=
- POWERSTATE_CHECK_INTERVAL)
- {
- menu_st->powerstate_last_time_us = menu_st->current_time_us;
- task_push_get_powerstate();
- }
- /* Get last recorded state */
- state = get_last_powerstate(&percent);
- /* Populate gfx_display_ctx_powerstate_t */
- powerstate->battery_enabled = (state != FRONTEND_POWERSTATE_NONE) &&
- (state != FRONTEND_POWERSTATE_NO_SOURCE);
- powerstate->percent = 0;
- powerstate->charging = false;
- if (powerstate->battery_enabled)
- {
- if (state == FRONTEND_POWERSTATE_CHARGING)
- powerstate->charging = true;
- if (percent > 0)
- powerstate->percent = (unsigned)percent;
- snprintf(powerstate->s, powerstate->len, "%u%%", powerstate->percent);
- }
- }
- /* Iterate the menu driver for one frame. */
- static bool menu_driver_iterate(
- struct rarch_state *p_rarch,
- enum menu_action action,
- retro_time_t current_time)
- {
- return (p_rarch->menu_driver_data &&
- generic_menu_iterate(
- p_rarch,
- p_rarch->menu_driver_data,
- p_rarch->menu_userdata, action,
- current_time) != -1);
- }
- int menu_driver_deferred_push_content_list(file_list_t *list)
- {
- struct rarch_state *p_rarch = &rarch_st;
- struct menu_state *menu_st = &p_rarch->menu_driver_state;
- menu_handle_t *menu_data = p_rarch->menu_driver_data;
- menu_list_t *menu_list = menu_st->entries.list;
- file_list_t *selection_buf = MENU_LIST_GET_SELECTION(menu_list, (unsigned)0);
- /* Must clear any existing menu search terms
- * when switching 'tabs', since doing so
- * bypasses standard backwards navigation
- * (i.e. 'cancel' actions would normally
- * pop the search stack - this will not
- * happen if we jump to a new list directly) */
- if (menu_data->search_terms)
- string_list_free(menu_data->search_terms);
- menu_data->search_terms = NULL;
- menu_st->selection_ptr = 0;
- if (!menu_driver_displaylist_push(
- p_rarch,
- menu_st,
- list,
- selection_buf))
- return -1;
- return 0;
- }
- bool menu_driver_list_cache(menu_ctx_list_t *list)
- {
- struct rarch_state *p_rarch = &rarch_st;
- if (!list || !p_rarch->menu_driver_ctx || !p_rarch->menu_driver_ctx->list_cache)
- return false;
- p_rarch->menu_driver_ctx->list_cache(p_rarch->menu_userdata,
- list->type, list->action);
- return true;
- }
- static enum menu_driver_id_type menu_driver_set_id(const char *driver_name)
- {
- if (!string_is_empty(driver_name))
- {
- if (string_is_equal(driver_name, "rgui"))
- return MENU_DRIVER_ID_RGUI;
- else if (string_is_equal(driver_name, "ozone"))
- return MENU_DRIVER_ID_OZONE;
- else if (string_is_equal(driver_name, "glui"))
- return MENU_DRIVER_ID_GLUI;
- else if (string_is_equal(driver_name, "xmb"))
- return MENU_DRIVER_ID_XMB;
- else if (string_is_equal(driver_name, "stripes"))
- return MENU_DRIVER_ID_STRIPES;
- }
- return MENU_DRIVER_ID_UNKNOWN;
- }
- static bool generic_menu_init_list(struct menu_state *menu_st)
- {
- menu_displaylist_info_t info;
- menu_list_t *menu_list = menu_st->entries.list;
- file_list_t *menu_stack = NULL;
- file_list_t *selection_buf = NULL;
- if (menu_list)
- {
- menu_stack = MENU_LIST_GET(menu_list, (unsigned)0);
- selection_buf = MENU_LIST_GET_SELECTION(menu_list, (unsigned)0);
- }
- menu_displaylist_info_init(&info);
- info.label = strdup(
- msg_hash_to_str(MENU_ENUM_LABEL_MAIN_MENU));
- info.enum_idx = MENU_ENUM_LABEL_MAIN_MENU;
- menu_entries_append_enum(menu_stack,
- info.path,
- info.label,
- MENU_ENUM_LABEL_MAIN_MENU,
- info.type, info.flags, 0);
- info.list = selection_buf;
- if (menu_displaylist_ctl(DISPLAYLIST_MAIN_MENU, &info))
- menu_displaylist_process(&info);
- menu_displaylist_info_free(&info);
- return true;
- }
- static bool menu_driver_init_internal(
- gfx_display_t *p_disp,
- struct rarch_state *p_rarch,
- settings_t *settings,
- bool video_is_threaded)
- {
- if (p_rarch->menu_driver_ctx)
- {
- const char *ident = p_rarch->menu_driver_ctx->ident;
- /* ID must be set first, since it is required for
- * the proper determination of pixel/dpi scaling
- * parameters (and some menu drivers fetch the
- * current pixel/dpi scale during 'menu_driver_ctx->init()') */
- if (ident)
- p_disp->menu_driver_id = menu_driver_set_id(ident);
- else
- p_disp->menu_driver_id = MENU_DRIVER_ID_UNKNOWN;
- if (p_rarch->menu_driver_ctx->init)
- {
- p_rarch->menu_driver_data = (menu_handle_t*)
- p_rarch->menu_driver_ctx->init(&p_rarch->menu_userdata,
- video_is_threaded);
- p_rarch->menu_driver_data->userdata = p_rarch->menu_userdata;
- p_rarch->menu_driver_data->driver_ctx = p_rarch->menu_driver_ctx;
- }
- }
- if (!p_rarch->menu_driver_data || !menu_init(
- &p_rarch->menu_driver_state,
- &p_rarch->dialog_st,
- p_rarch->menu_driver_ctx,
- &p_rarch->menu_input_state,
- &p_rarch->menu_input_pointer_hw_state,
- settings))
- return false;
- gfx_display_init();
- /* TODO/FIXME - can we get rid of this? Is this needed? */
- configuration_set_string(settings,
- settings->arrays.menu_driver, p_rarch->menu_driver_ctx->ident);
- if (p_rarch->menu_driver_ctx->lists_init)
- {
- if (!p_rarch->menu_driver_ctx->lists_init(p_rarch->menu_driver_data))
- return false;
- }
- else
- generic_menu_init_list(&p_rarch->menu_driver_state);
- return true;
- }
- bool menu_driver_init(bool video_is_threaded)
- {
- struct rarch_state *p_rarch = &rarch_st;
- gfx_display_t *p_disp = &p_rarch->dispgfx;
- command_event(CMD_EVENT_CORE_INFO_INIT, NULL);
- command_event(CMD_EVENT_LOAD_CORE_PERSIST, NULL);
- if ( p_rarch->menu_driver_data ||
- menu_driver_init_internal(
- &p_rarch->dispgfx,
- p_rarch,
- p_rarch->configuration_settings,
- video_is_threaded))
- {
- if (p_rarch->menu_driver_ctx && p_rarch->menu_driver_ctx->context_reset)
- {
- p_rarch->menu_driver_ctx->context_reset(p_rarch->menu_userdata,
- video_is_threaded);
- return true;
- }
- }
- /* If driver initialisation failed, must reset
- * driver id to 'unknown' */
- p_disp->menu_driver_id = MENU_DRIVER_ID_UNKNOWN;
- return false;
- }
- void menu_driver_navigation_set(bool scroll)
- {
- struct rarch_state *p_rarch = &rarch_st;
- if (p_rarch->menu_driver_ctx->navigation_set)
- p_rarch->menu_driver_ctx->navigation_set(p_rarch->menu_userdata, scroll);
- }
- void menu_driver_populate_entries(menu_displaylist_info_t *info)
- {
- struct rarch_state *p_rarch = &rarch_st;
- if (p_rarch->menu_driver_ctx && p_rarch->menu_driver_ctx->populate_entries)
- p_rarch->menu_driver_ctx->populate_entries(
- p_rarch->menu_userdata, info->path,
- info->label, info->type);
- }
- bool menu_driver_push_list(menu_ctx_displaylist_t *disp_list)
- {
- struct rarch_state *p_rarch = &rarch_st;
- if (p_rarch->menu_driver_ctx->list_push)
- if (p_rarch->menu_driver_ctx->list_push(
- p_rarch->menu_driver_data,
- p_rarch->menu_userdata,
- disp_list->info, disp_list->type) == 0)
- return true;
- return false;
- }
- void menu_driver_set_thumbnail_system(char *s, size_t len)
- {
- struct rarch_state *p_rarch = &rarch_st;
- if ( p_rarch->menu_driver_ctx
- && p_rarch->menu_driver_ctx->set_thumbnail_system)
- p_rarch->menu_driver_ctx->set_thumbnail_system(
- p_rarch->menu_userdata, s, len);
- }
- void menu_driver_get_thumbnail_system(char *s, size_t len)
- {
- struct rarch_state *p_rarch = &rarch_st;
- if ( p_rarch->menu_driver_ctx
- && p_rarch->menu_driver_ctx->get_thumbnail_system)
- p_rarch->menu_driver_ctx->get_thumbnail_system(
- p_rarch->menu_userdata, s, len);
- }
- void menu_driver_set_thumbnail_content(char *s, size_t len)
- {
- struct rarch_state *p_rarch = &rarch_st;
- if ( p_rarch->menu_driver_ctx
- && p_rarch->menu_driver_ctx->set_thumbnail_content)
- p_rarch->menu_driver_ctx->set_thumbnail_content(
- p_rarch->menu_userdata, s);
- }
- /* Teardown function for the menu driver. */
- static void menu_driver_destroy(
- struct rarch_state *p_rarch,
- struct menu_state *menu_st)
- {
- menu_st->pending_quick_menu = false;
- menu_st->prevent_populate = false;
- menu_st->data_own = false;
- p_rarch->menu_driver_ctx = NULL;
- p_rarch->menu_userdata = NULL;
- }
- bool menu_driver_list_get_entry(menu_ctx_list_t *list)
- {
- struct rarch_state *p_rarch = &rarch_st;
- if ( !p_rarch->menu_driver_ctx ||
- !p_rarch->menu_driver_ctx->list_get_entry)
- {
- list->entry = NULL;
- return false;
- }
- list->entry = p_rarch->menu_driver_ctx->list_get_entry(
- p_rarch->menu_userdata,
- list->type, (unsigned int)list->idx);
- return true;
- }
- bool menu_driver_list_get_selection(menu_ctx_list_t *list)
- {
- struct rarch_state *p_rarch = &rarch_st;
- if ( !p_rarch->menu_driver_ctx ||
- !p_rarch->menu_driver_ctx->list_get_selection)
- {
- list->selection = 0;
- return false;
- }
- list->selection = p_rarch->menu_driver_ctx->list_get_selection(
- p_rarch->menu_userdata);
- return true;
- }
- bool menu_driver_list_get_size(menu_ctx_list_t *list)
- {
- struct rarch_state *p_rarch = &rarch_st;
- if ( !p_rarch->menu_driver_ctx ||
- !p_rarch->menu_driver_ctx->list_get_size)
- {
- list->size = 0;
- return false;
- }
- list->size = p_rarch->menu_driver_ctx->list_get_size(
- p_rarch->menu_userdata, list->type);
- return true;
- }
- retro_time_t menu_driver_get_current_time(void)
- {
- struct rarch_state *p_rarch = &rarch_st;
- struct menu_state *menu_st = &p_rarch->menu_driver_state;
- return menu_st->current_time_us;
- }
- static void menu_driver_list_free(
- const menu_ctx_driver_t *menu_driver_ctx,
- menu_ctx_list_t *list)
- {
- if (menu_driver_ctx)
- if (menu_driver_ctx->list_free)
- menu_driver_ctx->list_free(
- list->list, list->idx, list->list_size);
- if (list->list)
- {
- file_list_free_userdata (list->list, list->idx);
- file_list_free_actiondata(list->list, list->idx);
- }
- }
- /* Returns true if search filter is enabled
- * for the specified menu list */
- bool menu_driver_search_filter_enabled(const char *label, unsigned type)
- {
- bool filter_enabled = false;
- /* > Check for playlists */
- filter_enabled = (type == MENU_SETTING_HORIZONTAL_MENU) ||
- (type == MENU_HISTORY_TAB) ||
- (type == MENU_FAVORITES_TAB) ||
- (type == MENU_IMAGES_TAB) ||
- (type == MENU_MUSIC_TAB) ||
- (type == MENU_VIDEO_TAB) ||
- (type == FILE_TYPE_PLAYLIST_COLLECTION);
- if (!filter_enabled && !string_is_empty(label))
- filter_enabled = string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_LOAD_CONTENT_HISTORY)) ||
- string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_DEFERRED_FAVORITES_LIST)) ||
- string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_DEFERRED_IMAGES_LIST)) ||
- string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_DEFERRED_MUSIC_LIST)) ||
- string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_DEFERRED_VIDEO_LIST)) ||
- /* > Check for core updater */
- string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_DEFERRED_CORE_UPDATER_LIST));
- return filter_enabled;
- }
- bool menu_driver_search_push(const char *search_term)
- {
- struct rarch_state *p_rarch = &rarch_st;
- menu_handle_t *menu = p_rarch->menu_driver_data;
- union string_list_elem_attr attr;
- if (!menu || string_is_empty(search_term))
- return false;
- /* Initialise list, if required */
- if (!menu->search_terms)
- {
- menu->search_terms = string_list_new();
- if (!menu->search_terms)
- return false;
- }
- /* Check whether search term already exists */
- if (string_list_find_elem(menu->search_terms, search_term))
- return false;
- /* Add search term */
- attr.i = 0;
- if (!string_list_append(menu->search_terms,
- search_term, attr))
- return false;
- return true;
- }
- bool menu_driver_search_pop(void)
- {
- struct rarch_state *p_rarch = &rarch_st;
- menu_handle_t *menu = p_rarch->menu_driver_data;
- union string_list_elem_attr attr = {0};
- size_t element_index;
- if (!menu || !menu->search_terms)
- return false;
- /* If we have a 'broken' list, free it
- * (this cannot actually happen, but if
- * we didn't free the list in this case
- * then menu navigation could get 'stuck') */
- if ((menu->search_terms->size < 1) ||
- !menu->search_terms->elems)
- goto free_list;
- /* Get index of last element in the list */
- element_index = menu->search_terms->size - 1;
- /* If this is the only element, free the
- * entire list */
- if (element_index == 0)
- goto free_list;
- /* Otherwise, 'reset' the element... */
- if (menu->search_terms->elems[element_index].data)
- {
- free(menu->search_terms->elems[element_index].data);
- menu->search_terms->elems[element_index].data = NULL;
- }
- if (menu->search_terms->elems[element_index].userdata)
- {
- free(menu->search_terms->elems[element_index].userdata);
- menu->search_terms->elems[element_index].userdata = NULL;
- }
- menu->search_terms->elems[element_index].attr = attr;
- /* ...and decrement the list size */
- menu->search_terms->size--;
- return true;
- free_list:
- string_list_free(menu->search_terms);
- menu->search_terms = NULL;
- return true;
- }
- struct string_list *menu_driver_search_get_terms(void)
- {
- struct rarch_state *p_rarch = &rarch_st;
- menu_handle_t *menu = p_rarch->menu_driver_data;
- if (!menu)
- return NULL;
- return menu->search_terms;
- }
- /* Convenience function: Appends list of current
- * search terms to specified string */
- void menu_driver_search_append_terms_string(char *s, size_t len)
- {
- struct rarch_state *p_rarch = &rarch_st;
- menu_handle_t *menu = p_rarch->menu_driver_data;
- if (menu &&
- menu->search_terms &&
- (menu->search_terms->size > 0) &&
- s)
- {
- char search_str[512];
- search_str[0] = '\0';
- string_list_join_concat(search_str, sizeof(search_str),
- menu->search_terms, " > ");
- strlcat(s, " > ", len);
- strlcat(s, search_str, len);
- }
- }
- #if defined(HAVE_CG) || defined(HAVE_GLSL) || defined(HAVE_SLANG) || defined(HAVE_HLSL)
- static void menu_driver_set_last_shader_path_int(
- const char *shader_path,
- enum rarch_shader_type *type,
- char *shader_dir, size_t dir_len,
- char *shader_file, size_t file_len)
- {
- const char *file_name = NULL;
- if (!type ||
- !shader_dir ||
- (dir_len < 1) ||
- !shader_file ||
- (file_len < 1))
- return;
- /* Reset existing cache */
- *type = RARCH_SHADER_NONE;
- shader_dir[0] = '\0';
- shader_file[0] = '\0';
- /* If path is empty, do nothing */
- if (string_is_empty(shader_path))
- return;
- /* Get shader type */
- *type = video_shader_parse_type(shader_path);
- /* If type is invalid, do nothing */
- if (*type == RARCH_SHADER_NONE)
- return;
- /* Cache parent directory */
- fill_pathname_parent_dir(shader_dir, shader_path, dir_len);
- /* If parent directory is empty, then file name
- * is only valid if 'shader_path' refers to an
- * existing file in the root of the file system */
- if (string_is_empty(shader_dir) &&
- !path_is_valid(shader_path))
- return;
- /* Cache file name */
- file_name = path_basename(shader_path);
- if (!string_is_empty(file_name))
- strlcpy(shader_file, file_name, file_len);
- }
- void menu_driver_set_last_shader_preset_path(const char *path)
- {
- struct rarch_state *p_rarch = &rarch_st;
- menu_handle_t *menu = p_rarch->menu_driver_data;
- if (!menu)
- return;
- menu_driver_set_last_shader_path_int(
- path,
- &menu->last_shader_selection.preset_type,
- menu->last_shader_selection.preset_dir,
- sizeof(menu->last_shader_selection.preset_dir),
- menu->last_shader_selection.preset_file_name,
- sizeof(menu->last_shader_selection.preset_file_name));
- }
- void menu_driver_set_last_shader_pass_path(const char *path)
- {
- struct rarch_state *p_rarch = &rarch_st;
- menu_handle_t *menu = p_rarch->menu_driver_data;
- if (!menu)
- return;
- menu_driver_set_last_shader_path_int(
- path,
- &menu->last_shader_selection.pass_type,
- menu->last_shader_selection.pass_dir,
- sizeof(menu->last_shader_selection.pass_dir),
- menu->last_shader_selection.pass_file_name,
- sizeof(menu->last_shader_selection.pass_file_name));
- }
- enum rarch_shader_type menu_driver_get_last_shader_preset_type(void)
- {
- struct rarch_state *p_rarch = &rarch_st;
- menu_handle_t *menu = p_rarch->menu_driver_data;
- if (!menu)
- return RARCH_SHADER_NONE;
- return menu->last_shader_selection.preset_type;
- }
- enum rarch_shader_type menu_driver_get_last_shader_pass_type(void)
- {
- struct rarch_state *p_rarch = &rarch_st;
- menu_handle_t *menu = p_rarch->menu_driver_data;
- if (!menu)
- return RARCH_SHADER_NONE;
- return menu->last_shader_selection.pass_type;
- }
- void menu_driver_get_last_shader_path_int(
- struct rarch_state *p_rarch, enum rarch_shader_type type,
- const char *shader_dir, const char *shader_file_name,
- const char **dir_out, const char **file_name_out)
- {
- settings_t *settings = p_rarch->configuration_settings;
- bool remember_last_dir = settings->bools.video_shader_remember_last_dir;
- const char *video_shader_dir = settings->paths.directory_video_shader;
- /* File name is NULL by default */
- if (file_name_out)
- *file_name_out = NULL;
- /* If any of the following are true:
- * - Directory caching is disabled
- * - No directory has been cached
- * - Cached directory is invalid
- * - Last selected shader is incompatible with
- * the current video driver
- * ...use default settings */
- if (!remember_last_dir ||
- (type == RARCH_SHADER_NONE) ||
- string_is_empty(shader_dir) ||
- !path_is_directory(shader_dir) ||
- !video_shader_is_supported(type))
- {
- if (dir_out)
- *dir_out = video_shader_dir;
- return;
- }
- /* Assign last set directory */
- if (dir_out)
- *dir_out = shader_dir;
- /* Assign file name */
- if (file_name_out &&
- !string_is_empty(shader_file_name))
- *file_name_out = shader_file_name;
- }
- void menu_driver_get_last_shader_preset_path(
- const char **directory, const char **file_name)
- {
- struct rarch_state *p_rarch = &rarch_st;
- menu_handle_t *menu = p_rarch->menu_driver_data;
- enum rarch_shader_type type = RARCH_SHADER_NONE;
- const char *shader_dir = NULL;
- const char *shader_file_name = NULL;
- if (menu)
- {
- type = menu->last_shader_selection.preset_type;
- shader_dir = menu->last_shader_selection.preset_dir;
- shader_file_name = menu->last_shader_selection.preset_file_name;
- }
- menu_driver_get_last_shader_path_int(p_rarch, type,
- shader_dir, shader_file_name,
- directory, file_name);
- }
- void menu_driver_get_last_shader_pass_path(
- const char **directory, const char **file_name)
- {
- struct rarch_state *p_rarch = &rarch_st;
- menu_handle_t *menu = p_rarch->menu_driver_data;
- enum rarch_shader_type type = RARCH_SHADER_NONE;
- const char *shader_dir = NULL;
- const char *shader_file_name = NULL;
- if (menu)
- {
- type = menu->last_shader_selection.pass_type;
- shader_dir = menu->last_shader_selection.pass_dir;
- shader_file_name = menu->last_shader_selection.pass_file_name;
- }
- menu_driver_get_last_shader_path_int(p_rarch, type,
- shader_dir, shader_file_name,
- directory, file_name);
- }
- #endif
- const char *menu_driver_get_last_start_directory(void)
- {
- struct rarch_state *p_rarch = &rarch_st;
- menu_handle_t *menu = p_rarch->menu_driver_data;
- settings_t *settings = p_rarch->configuration_settings;
- bool use_last = settings->bools.use_last_start_directory;
- const char *default_directory = settings->paths.directory_menu_content;
- /* Return default directory if there is no
- * last directory or it's invalid */
- if (!menu ||
- !use_last ||
- string_is_empty(menu->last_start_content.directory) ||
- !path_is_directory(menu->last_start_content.directory))
- return default_directory;
- return menu->last_start_content.directory;
- }
- const char *menu_driver_get_last_start_file_name(void)
- {
- struct rarch_state *p_rarch = &rarch_st;
- menu_handle_t *menu = p_rarch->menu_driver_data;
- settings_t *settings = p_rarch->configuration_settings;
- bool use_last = settings->bools.use_last_start_directory;
- /* Return NULL if there is no last 'file name' */
- if (!menu ||
- !use_last ||
- string_is_empty(menu->last_start_content.file_name))
- return NULL;
- return menu->last_start_content.file_name;
- }
- void menu_driver_set_last_start_content(const char *start_content_path)
- {
- struct rarch_state *p_rarch = &rarch_st;
- menu_handle_t *menu = p_rarch->menu_driver_data;
- settings_t *settings = p_rarch->configuration_settings;
- bool use_last = settings->bools.use_last_start_directory;
- const char *archive_delim = NULL;
- const char *file_name = NULL;
- char archive_path[PATH_MAX_LENGTH];
- if (!menu)
- return;
- /* Reset existing cache */
- menu->last_start_content.directory[0] = '\0';
- menu->last_start_content.file_name[0] = '\0';
- /* If 'use_last_start_directory' is disabled or
- * path is empty, do nothing */
- if (!use_last ||
- string_is_empty(start_content_path))
- return;
- /* Cache directory */
- fill_pathname_parent_dir(menu->last_start_content.directory,
- start_content_path, sizeof(menu->last_start_content.directory));
- /* Cache file name */
- archive_delim = path_get_archive_delim(start_content_path);
- if (archive_delim)
- {
- /* If path references a file inside an
- * archive, must extract the string segment
- * before the archive delimiter (i.e. path of
- * 'parent' archive file) */
- size_t len;
- archive_path[0] = '\0';
- len = (size_t)(1 + archive_delim - start_content_path);
- len = (len < PATH_MAX_LENGTH) ? len : PATH_MAX_LENGTH;
- strlcpy(archive_path, start_content_path, len * sizeof(char));
- file_name = path_basename(archive_path);
- }
- else
- file_name = path_basename(start_content_path);
- if (!string_is_empty(file_name))
- strlcpy(menu->last_start_content.file_name, file_name,
- sizeof(menu->last_start_content.file_name));
- }
- const char *menu_driver_get_pending_selection(void)
- {
- struct rarch_state *p_rarch = &rarch_st;
- struct menu_state *menu_st = &p_rarch->menu_driver_state;
- return menu_st->pending_selection;
- }
- void menu_driver_set_pending_selection(const char *pending_selection)
- {
- struct rarch_state *p_rarch = &rarch_st;
- struct menu_state *menu_st = &p_rarch->menu_driver_state;
- char *selection = menu_st->pending_selection;
- /* Reset existing cache */
- selection[0] = '\0';
- /* If path is empty, do nothing */
- if (string_is_empty(pending_selection))
- return;
- strlcpy(selection, pending_selection,
- sizeof(menu_st->pending_selection));
- }
- bool menu_driver_ctl(enum rarch_menu_ctl_state state, void *data)
- {
- struct rarch_state *p_rarch = &rarch_st;
- gfx_display_t *p_disp = &p_rarch->dispgfx;
- menu_handle_t *menu_data = p_rarch->menu_driver_data;
- struct menu_state *menu_st = &p_rarch->menu_driver_state;
- switch (state)
- {
- case RARCH_MENU_CTL_SET_PENDING_QUICK_MENU:
- menu_entries_flush_stack(NULL, MENU_SETTINGS);
- menu_st->pending_quick_menu = true;
- break;
- case RARCH_MENU_CTL_FIND_DRIVER:
- {
- settings_t *settings = p_rarch->configuration_settings;
- int i = (int)driver_find_index(
- "menu_driver",
- settings->arrays.menu_driver);
- if (i >= 0)
- p_rarch->menu_driver_ctx = (const menu_ctx_driver_t*)
- menu_ctx_drivers[i];
- else
- {
- if (verbosity_is_enabled())
- {
- unsigned d;
- RARCH_WARN("Couldn't find any menu driver named \"%s\"\n",
- settings->arrays.menu_driver);
- RARCH_LOG_OUTPUT("Available menu drivers are:\n");
- for (d = 0; menu_ctx_drivers[d]; d++)
- {
- if (menu_ctx_drivers[d])
- {
- RARCH_LOG_OUTPUT("\t%s\n", menu_ctx_drivers[d]->ident);
- }
- }
- RARCH_WARN("Going to default to first menu driver...\n");
- }
- p_rarch->menu_driver_ctx = (const menu_ctx_driver_t*)
- menu_ctx_drivers[0];
- if (!p_rarch->menu_driver_ctx)
- return false;
- }
- }
- break;
- case RARCH_MENU_CTL_SET_PREVENT_POPULATE:
- menu_st->prevent_populate = true;
- break;
- case RARCH_MENU_CTL_UNSET_PREVENT_POPULATE:
- menu_st->prevent_populate = false;
- break;
- case RARCH_MENU_CTL_IS_PREVENT_POPULATE:
- return menu_st->prevent_populate;
- case RARCH_MENU_CTL_DEINIT:
- if ( p_rarch->menu_driver_ctx
- && p_rarch->menu_driver_ctx->context_destroy)
- p_rarch->menu_driver_ctx->context_destroy(p_rarch->menu_userdata);
- if (menu_st->data_own)
- return true;
- playlist_free_cached();
- #if defined(HAVE_CG) || defined(HAVE_GLSL) || defined(HAVE_SLANG) || defined(HAVE_HLSL)
- menu_shader_manager_free(p_rarch);
- #endif
- #ifdef HAVE_NETWORKING
- core_updater_list_free_cached();
- #endif
- #if defined(HAVE_MENU) && defined(HAVE_LIBRETRODB)
- menu_explore_free();
- #endif
- if (p_rarch->menu_driver_data)
- {
- unsigned i;
- menu_st->scroll.acceleration = 0;
- menu_st->selection_ptr = 0;
- menu_st->scroll.index_size = 0;
- for (i = 0; i < SCROLL_INDEX_SIZE; i++)
- menu_st->scroll.index_list[i] = 0;
- menu_input_reset(
- &p_rarch->menu_input_state,
- &p_rarch->menu_input_pointer_hw_state
- );
- if ( p_rarch->menu_driver_ctx
- && p_rarch->menu_driver_ctx->free)
- p_rarch->menu_driver_ctx->free(p_rarch->menu_userdata);
- if (p_rarch->menu_userdata)
- free(p_rarch->menu_userdata);
- p_rarch->menu_userdata = NULL;
- p_disp->menu_driver_id = MENU_DRIVER_ID_UNKNOWN;
- #ifndef HAVE_DYNAMIC
- if (frontend_driver_has_fork())
- #endif
- {
- rarch_system_info_t *system = &p_rarch->runloop_system;
- libretro_free_system_info(&system->info);
- memset(&system->info, 0, sizeof(struct retro_system_info));
- }
- gfx_animation_deinit(&p_rarch->anim);
- gfx_display_free();
- menu_entries_settings_deinit(menu_st);
- menu_entries_list_deinit(p_rarch->menu_driver_ctx, menu_st);
- if (p_rarch->menu_driver_data->core_buf)
- free(p_rarch->menu_driver_data->core_buf);
- p_rarch->menu_driver_data->core_buf = NULL;
- if (menu_data->search_terms)
- string_list_free(menu_data->search_terms);
- menu_data->search_terms = NULL;
- menu_st->entries_need_refresh = false;
- menu_st->entries_nonblocking_refresh = false;
- menu_st->entries.begin = 0;
- command_event(CMD_EVENT_HISTORY_DEINIT, NULL);
- rarch_favorites_deinit();
- p_rarch->dialog_st.pending_push = false;
- p_rarch->dialog_st.current_id = 0;
- p_rarch->dialog_st.current_type = MENU_DIALOG_NONE;
- free(p_rarch->menu_driver_data);
- }
- p_rarch->menu_driver_data = NULL;
- break;
- case RARCH_MENU_CTL_ENVIRONMENT:
- {
- menu_ctx_environment_t *menu_environ =
- (menu_ctx_environment_t*)data;
- if (p_rarch->menu_driver_ctx->environ_cb)
- {
- if (p_rarch->menu_driver_ctx->environ_cb(menu_environ->type,
- menu_environ->data, p_rarch->menu_userdata) == 0)
- return true;
- }
- }
- return false;
- case RARCH_MENU_CTL_POINTER_DOWN:
- {
- menu_ctx_pointer_t *point = (menu_ctx_pointer_t*)data;
- if (!p_rarch->menu_driver_ctx || !p_rarch->menu_driver_ctx->pointer_down)
- {
- point->retcode = 0;
- return false;
- }
- point->retcode = p_rarch->menu_driver_ctx->pointer_down(
- p_rarch->menu_userdata,
- point->x, point->y, point->ptr,
- point->cbs, point->entry, point->action);
- }
- break;
- case RARCH_MENU_CTL_POINTER_UP:
- {
- menu_ctx_pointer_t *point = (menu_ctx_pointer_t*)data;
- if (!p_rarch->menu_driver_ctx || !p_rarch->menu_driver_ctx->pointer_up)
- {
- point->retcode = 0;
- return false;
- }
- point->retcode = p_rarch->menu_driver_ctx->pointer_up(
- p_rarch->menu_userdata,
- point->x, point->y, point->ptr,
- point->gesture,
- point->cbs, point->entry, point->action);
- }
- break;
- case RARCH_MENU_CTL_OSK_PTR_AT_POS:
- {
- unsigned width = 0;
- unsigned height = 0;
- menu_ctx_pointer_t *point = (menu_ctx_pointer_t*)data;
- if (!p_rarch->menu_driver_ctx || !p_rarch->menu_driver_ctx->osk_ptr_at_pos)
- {
- point->retcode = 0;
- return false;
- }
- video_driver_get_size(&width, &height);
- point->retcode = p_rarch->menu_driver_ctx->osk_ptr_at_pos(
- p_rarch->menu_userdata,
- point->x, point->y, width, height);
- }
- break;
- case RARCH_MENU_CTL_UPDATE_THUMBNAIL_PATH:
- {
- size_t selection = menu_st->selection_ptr;
- if (!p_rarch->menu_driver_ctx || !p_rarch->menu_driver_ctx->update_thumbnail_path)
- return false;
- p_rarch->menu_driver_ctx->update_thumbnail_path(
- p_rarch->menu_userdata, (unsigned)selection, 'L');
- p_rarch->menu_driver_ctx->update_thumbnail_path(
- p_rarch->menu_userdata, (unsigned)selection, 'R');
- }
- break;
- case RARCH_MENU_CTL_UPDATE_THUMBNAIL_IMAGE:
- {
- if (!p_rarch->menu_driver_ctx || !p_rarch->menu_driver_ctx->update_thumbnail_image)
- return false;
- p_rarch->menu_driver_ctx->update_thumbnail_image(p_rarch->menu_userdata);
- }
- break;
- case RARCH_MENU_CTL_REFRESH_THUMBNAIL_IMAGE:
- {
- unsigned *i = (unsigned*)data;
- if (!i || !p_rarch->menu_driver_ctx ||
- !p_rarch->menu_driver_ctx->refresh_thumbnail_image)
- return false;
- p_rarch->menu_driver_ctx->refresh_thumbnail_image(
- p_rarch->menu_userdata, *i);
- }
- break;
- case RARCH_MENU_CTL_UPDATE_SAVESTATE_THUMBNAIL_PATH:
- {
- size_t selection = menu_st->selection_ptr;
- if ( !p_rarch->menu_driver_ctx ||
- !p_rarch->menu_driver_ctx->update_savestate_thumbnail_path)
- return false;
- p_rarch->menu_driver_ctx->update_savestate_thumbnail_path(
- p_rarch->menu_userdata, (unsigned)selection);
- }
- break;
- case RARCH_MENU_CTL_UPDATE_SAVESTATE_THUMBNAIL_IMAGE:
- if ( !p_rarch->menu_driver_ctx ||
- !p_rarch->menu_driver_ctx->update_savestate_thumbnail_image)
- return false;
- p_rarch->menu_driver_ctx->update_savestate_thumbnail_image(
- p_rarch->menu_userdata);
- break;
- case MENU_NAVIGATION_CTL_CLEAR:
- {
- bool *pending_push = (bool*)data;
- /* Always set current selection to first entry */
- menu_st->selection_ptr = 0;
- /* menu_driver_navigation_set() will be called
- * at the next 'push'.
- * If a push is *not* pending, have to do it here
- * instead */
- if (!(*pending_push))
- {
- menu_driver_navigation_set(true);
- if (p_rarch->menu_driver_ctx->navigation_clear)
- p_rarch->menu_driver_ctx->navigation_clear(
- p_rarch->menu_userdata, *pending_push);
- }
- }
- break;
- case MENU_NAVIGATION_CTL_SET_LAST:
- {
- size_t menu_list_size = menu_st->entries.list ? MENU_LIST_GET_SELECTION(menu_st->entries.list, 0)->size : 0;
- size_t new_selection = menu_list_size - 1;
- menu_st->selection_ptr = new_selection;
- if (p_rarch->menu_driver_ctx->navigation_set_last)
- p_rarch->menu_driver_ctx->navigation_set_last(p_rarch->menu_userdata);
- }
- break;
- case MENU_NAVIGATION_CTL_GET_SCROLL_ACCEL:
- {
- size_t *sel = (size_t*)data;
- if (!sel)
- return false;
- *sel = menu_st->scroll.acceleration;
- }
- break;
- default:
- case RARCH_MENU_CTL_NONE:
- break;
- }
- return true;
- }
- #endif
- /**
- * find_driver_nonempty:
- * @label : string of driver type to be found.
- * @i : index of driver.
- * @str : identifier name of the found driver
- * gets written to this string.
- * @len : size of @str.
- *
- * Find driver based on @label.
- *
- * Returns: NULL if no driver based on @label found, otherwise
- * pointer to driver.
- **/
- static const void *find_driver_nonempty(
- const char *label, int i,
- char *s, size_t len)
- {
- if (string_is_equal(label, "camera_driver"))
- {
- if (camera_drivers[i])
- {
- const char *ident = camera_drivers[i]->ident;
- strlcpy(s, ident, len);
- return camera_drivers[i];
- }
- }
- else if (string_is_equal(label, "location_driver"))
- {
- if (location_drivers[i])
- {
- const char *ident = location_drivers[i]->ident;
- strlcpy(s, ident, len);
- return location_drivers[i];
- }
- }
- #ifdef HAVE_MENU
- else if (string_is_equal(label, "menu_driver"))
- {
- if (menu_ctx_drivers[i])
- {
- const char *ident = menu_ctx_drivers[i]->ident;
- strlcpy(s, ident, len);
- return menu_ctx_drivers[i];
- }
- }
- #endif
- else if (string_is_equal(label, "input_driver"))
- {
- if (input_drivers[i])
- {
- const char *ident = input_drivers[i]->ident;
- strlcpy(s, ident, len);
- return input_drivers[i];
- }
- }
- else if (string_is_equal(label, "input_joypad_driver"))
- {
- if (joypad_drivers[i])
- {
- const char *ident = joypad_drivers[i]->ident;
- strlcpy(s, ident, len);
- return joypad_drivers[i];
- }
- }
- else if (string_is_equal(label, "video_driver"))
- {
- if (video_drivers[i])
- {
- const char *ident = video_drivers[i]->ident;
- strlcpy(s, ident, len);
- return video_drivers[i];
- }
- }
- else if (string_is_equal(label, "audio_driver"))
- {
- if (audio_drivers[i])
- {
- const char *ident = audio_drivers[i]->ident;
- strlcpy(s, ident, len);
- return audio_drivers[i];
- }
- }
- else if (string_is_equal(label, "record_driver"))
- {
- if (record_drivers[i])
- {
- const char *ident = record_drivers[i]->ident;
- strlcpy(s, ident, len);
- return record_drivers[i];
- }
- }
- else if (string_is_equal(label, "midi_driver"))
- {
- if (midi_driver_find_handle(i))
- {
- const char *ident = midi_drivers[i]->ident;
- strlcpy(s, ident, len);
- return midi_drivers[i];
- }
- }
- else if (string_is_equal(label, "audio_resampler_driver"))
- {
- if (audio_resampler_driver_find_handle(i))
- {
- const char *ident = audio_resampler_driver_find_ident(i);
- strlcpy(s, ident, len);
- return audio_resampler_driver_find_handle(i);
- }
- }
- else if (string_is_equal(label, "bluetooth_driver"))
- {
- if (bluetooth_drivers[i])
- {
- const char *ident = bluetooth_drivers[i]->ident;
- strlcpy(s, ident, len);
- return bluetooth_drivers[i];
- }
- }
- else if (string_is_equal(label, "wifi_driver"))
- {
- if (wifi_drivers[i])
- {
- const char *ident = wifi_drivers[i]->ident;
- strlcpy(s, ident, len);
- return wifi_drivers[i];
- }
- }
- return NULL;
- }
- #if defined(HAVE_CG) || defined(HAVE_GLSL) || defined(HAVE_SLANG) || defined(HAVE_HLSL)
- struct video_shader *menu_shader_get(void)
- {
- struct rarch_state *p_rarch = &rarch_st;
- if (video_shader_any_supported())
- if (p_rarch)
- return p_rarch->menu_driver_shader;
- return NULL;
- }
- void menu_shader_manager_free(void *data)
- {
- struct rarch_state *p_rarch = (struct rarch_state*)data;
- if (p_rarch->menu_driver_shader)
- free(p_rarch->menu_driver_shader);
- p_rarch->menu_driver_shader = NULL;
- }
- /**
- * menu_shader_manager_init:
- *
- * Initializes shader manager.
- **/
- bool menu_shader_manager_init(void)
- {
- struct rarch_state *p_rarch = &rarch_st;
- enum rarch_shader_type type = RARCH_SHADER_NONE;
- bool ret = true;
- bool is_preset = false;
- const char *path_shader = NULL;
- struct video_shader *menu_shader = NULL;
- /* We get the shader preset directly from the video driver, so that
- * we are in sync with it (it could fail loading an auto-shader)
- * If we can't (e.g. get_current_shader is not implemented),
- * we'll load retroarch_get_shader_preset() like always */
- video_shader_ctx_t shader_info = {0};
- video_shader_driver_get_current_shader(&shader_info);
- if (shader_info.data)
- /* Use the path of the originally loaded preset because it could
- * have been a preset with a #reference in it to another preset */
- path_shader = shader_info.data->loaded_preset_path;
- else
- path_shader = retroarch_get_shader_preset();
- menu_shader_manager_free(p_rarch);
- menu_shader = (struct video_shader*)
- calloc(1, sizeof(*menu_shader));
- if (!menu_shader)
- {
- ret = false;
- goto end;
- }
- if (string_is_empty(path_shader))
- goto end;
- type = video_shader_get_type_from_ext(path_get_extension(path_shader),
- &is_preset);
- if (!video_shader_is_supported(type))
- {
- ret = false;
- goto end;
- }
- if (is_preset)
- {
- if (!video_shader_load_preset_into_shader(path_shader, menu_shader))
- {
- ret = false;
- goto end;
- }
- menu_shader->modified = false;
- }
- else
- {
- strlcpy(menu_shader->pass[0].source.path, path_shader,
- sizeof(menu_shader->pass[0].source.path));
- menu_shader->passes = 1;
- }
- end:
- p_rarch->menu_driver_shader = menu_shader;
- command_event(CMD_EVENT_SHADER_PRESET_LOADED, NULL);
- return ret;
- }
- /**
- * menu_shader_manager_set_preset:
- * @shader : Shader handle.
- * @type : Type of shader.
- * @preset_path : Preset path to load from.
- * @apply : Whether to apply the shader or just update shader information
- *
- * Sets shader preset.
- **/
- bool menu_shader_manager_set_preset(struct video_shader *shader,
- enum rarch_shader_type type, const char *preset_path, bool apply)
- {
- bool refresh = false;
- bool ret = false;
- if (apply && !retroarch_apply_shader(type, preset_path, true))
- goto clear;
- if (string_is_empty(preset_path))
- {
- ret = true;
- goto clear;
- }
- /* Load stored Preset into menu on success.
- * Used when a preset is directly loaded.
- * No point in updating when the Preset was
- * created from the menu itself. */
- if ( !shader ||
- !(video_shader_load_preset_into_shader(preset_path, shader)))
- goto end;
- RARCH_LOG("Menu shader set to: %s.\n", preset_path);
- ret = true;
- end:
- #ifdef HAVE_MENU
- menu_entries_ctl(MENU_ENTRIES_CTL_SET_REFRESH, &refresh);
- #endif
- command_event(CMD_EVENT_SHADER_PRESET_LOADED, NULL);
- return ret;
- clear:
- /* We don't want to disable shaders entirely here,
- * just reset number of passes
- * > Note: Disabling shaders at this point would in
- * fact be dangerous, since it changes the number of
- * entries in the shader options menu which can in
- * turn lead to the menu selection pointer going out
- * of bounds. This causes undefined behaviour/segfaults */
- menu_shader_manager_clear_num_passes(shader);
- command_event(CMD_EVENT_SHADER_PRESET_LOADED, NULL);
- return ret;
- }
- static bool menu_shader_manager_save_preset_internal(
- const struct video_shader *shader,
- const char *basename,
- const char *dir_video_shader,
- bool apply,
- const char **target_dirs,
- size_t num_target_dirs)
- {
- char fullname[PATH_MAX_LENGTH];
- char buffer[PATH_MAX_LENGTH];
- bool ret = false;
- enum rarch_shader_type type = RARCH_SHADER_NONE;
- char *preset_path = NULL;
- size_t i = 0;
- struct rarch_state *p_rarch = &rarch_st;
- settings_t *settings = p_rarch->configuration_settings;
- bool save_reference =
- settings->bools.video_shader_preset_save_reference_enable;
- fullname[0] = buffer[0] = '\0';
- if (!shader || !shader->passes)
- return false;
- type = menu_shader_manager_get_type(shader);
- if (type == RARCH_SHADER_NONE)
- return false;
- if (!string_is_empty(basename))
- {
- /* We are comparing against a fixed list of file
- * extensions, the longest (slangp) being 6 characters
- * in length. We therefore only need to extract the first
- * 7 characters from the extension of the input path
- * to correctly validate a match */
- char ext_lower[8];
- const char *ext = NULL;
- ext_lower[0] = '\0';
- strlcpy(fullname, basename, sizeof(fullname));
- /* Get file extension */
- ext = strrchr(basename, '.');
- /* Copy and convert to lower case */
- if (ext && (*(++ext) != '\0'))
- {
- strlcpy(ext_lower, ext, sizeof(ext_lower));
- string_to_lower(ext_lower);
- }
- /* Append extension automatically as appropriate. */
- if ( !string_is_equal(ext_lower, "cgp")
- && !string_is_equal(ext_lower, "glslp")
- && !string_is_equal(ext_lower, "slangp"))
- {
- const char *preset_ext = video_shader_get_preset_extension(type);
- strlcat(fullname, preset_ext, sizeof(fullname));
- }
- }
- else
- {
- strcpy_literal(fullname, "retroarch");
- strlcat(fullname,
- video_shader_get_preset_extension(type), sizeof(fullname));
- }
- if (path_is_absolute(fullname))
- {
- preset_path = fullname;
- ret = video_shader_write_preset(preset_path,
- dir_video_shader,
- shader, save_reference);
- if (ret)
- RARCH_LOG("[Shaders - Save Preset]: Saved shader preset to %s.\n", preset_path);
- else
- RARCH_ERR("[Shaders - Save Preset]: Failed writing shader preset to %s.\n", preset_path);
- }
- else
- {
- char basedir[PATH_MAX_LENGTH];
- for (i = 0; i < num_target_dirs; i++)
- {
- if (string_is_empty(target_dirs[i]))
- continue;
- fill_pathname_join(buffer, target_dirs[i],
- fullname, sizeof(buffer));
- strlcpy(basedir, buffer, sizeof(basedir));
- path_basedir(basedir);
- if (!path_is_directory(basedir))
- {
- ret = path_mkdir(basedir);
- if (!ret)
- {
- RARCH_WARN("[Shaders - Save Preset]: Failed to create preset directory %s.\n", basedir);
- continue;
- }
- }
- preset_path = buffer;
- ret = video_shader_write_preset(preset_path,
- dir_video_shader,
- shader, save_reference);
- if (ret)
- {
- RARCH_LOG("[Shaders - Save Preset]: Saved shader preset to %s.\n", preset_path);
- break;
- }
- else
- RARCH_WARN("[Shaders - Save Preset]: Failed writing shader preset to %s.\n", preset_path);
- }
- if (!ret)
- RARCH_ERR("[Shaders - Save Preset]: Failed to write shader preset. Make sure shader directory"
- " and/or config directory are writable.\n");
- }
- if (ret && apply)
- menu_shader_manager_set_preset(NULL, type, preset_path, true);
- return ret;
- }
- static bool menu_shader_manager_operate_auto_preset(
- enum auto_shader_operation op,
- const struct video_shader *shader,
- const char *dir_video_shader,
- const char *dir_menu_config,
- enum auto_shader_type type, bool apply)
- {
- char old_presets_directory[PATH_MAX_LENGTH];
- char config_directory[PATH_MAX_LENGTH];
- char tmp[PATH_MAX_LENGTH];
- char file[PATH_MAX_LENGTH];
- struct retro_system_info *system = runloop_get_libretro_system_info();
- const char *core_name = system ? system->library_name : NULL;
- const char *auto_preset_dirs[3] = {0};
- old_presets_directory[0] = config_directory[0] = tmp[0] = file[0] = '\0';
- if (type != SHADER_PRESET_GLOBAL && string_is_empty(core_name))
- return false;
- if (!path_is_empty(RARCH_PATH_CONFIG))
- fill_pathname_basedir(
- config_directory,
- path_get(RARCH_PATH_CONFIG),
- sizeof(config_directory));
- /* We are only including this directory for compatibility purposes with
- * versions 1.8.7 and older. */
- if (op != AUTO_SHADER_OP_SAVE && !string_is_empty(dir_video_shader))
- fill_pathname_join(
- old_presets_directory,
- dir_video_shader,
- "presets",
- sizeof(old_presets_directory));
- auto_preset_dirs[0] = dir_menu_config;
- auto_preset_dirs[1] = config_directory;
- auto_preset_dirs[2] = old_presets_directory;
- switch (type)
- {
- case SHADER_PRESET_GLOBAL:
- strcpy_literal(file, "global");
- break;
- case SHADER_PRESET_CORE:
- fill_pathname_join(file, core_name, core_name, sizeof(file));
- break;
- case SHADER_PRESET_PARENT:
- fill_pathname_parent_dir_name(tmp,
- path_get(RARCH_PATH_BASENAME), sizeof(tmp));
- fill_pathname_join(file, core_name, tmp, sizeof(file));
- break;
- case SHADER_PRESET_GAME:
- {
- const char *game_name =
- path_basename(path_get(RARCH_PATH_BASENAME));
- if (string_is_empty(game_name))
- return false;
- fill_pathname_join(file, core_name, game_name, sizeof(file));
- break;
- }
- default:
- return false;
- }
- switch (op)
- {
- case AUTO_SHADER_OP_SAVE:
- return menu_shader_manager_save_preset_internal(
- shader, file,
- dir_video_shader,
- apply,
- auto_preset_dirs,
- ARRAY_SIZE(auto_preset_dirs));
- case AUTO_SHADER_OP_REMOVE:
- {
- /* remove all supported auto-shaders of given type */
- char *end;
- size_t i, j, n, m;
- char preset_path[PATH_MAX_LENGTH];
- /* n = amount of relevant shader presets found
- * m = amount of successfully deleted shader presets */
- n = m = 0;
- for (i = 0; i < ARRAY_SIZE(auto_preset_dirs); i++)
- {
- if (string_is_empty(auto_preset_dirs[i]))
- continue;
- fill_pathname_join(preset_path,
- auto_preset_dirs[i], file, sizeof(preset_path));
- end = preset_path + strlen(preset_path);
- for (j = 0; j < ARRAY_SIZE(shader_types); j++)
- {
- const char *preset_ext;
- if (!video_shader_is_supported(shader_types[j]))
- continue;
- preset_ext = video_shader_get_preset_extension(shader_types[j]);
- strlcpy(end, preset_ext, sizeof(preset_path) - (end - preset_path));
- if (path_is_valid(preset_path))
- {
- n++;
- if (!filestream_delete(preset_path))
- {
- m++;
- RARCH_LOG("[Shaders]: Deleted shader preset from \"%s\".\n", preset_path);
- }
- else
- RARCH_WARN("[Shaders]: Failed to remove shader preset at \"%s\".\n", preset_path);
- }
- }
- }
- return n == m;
- }
- case AUTO_SHADER_OP_EXISTS:
- {
- /* test if any supported auto-shaders of given type exists */
- char *end;
- size_t i, j;
- char preset_path[PATH_MAX_LENGTH];
- for (i = 0; i < ARRAY_SIZE(auto_preset_dirs); i++)
- {
- if (string_is_empty(auto_preset_dirs[i]))
- continue;
- fill_pathname_join(preset_path,
- auto_preset_dirs[i], file, sizeof(preset_path));
- end = preset_path + strlen(preset_path);
- for (j = 0; j < ARRAY_SIZE(shader_types); j++)
- {
- const char *preset_ext;
- if (!video_shader_is_supported(shader_types[j]))
- continue;
- preset_ext = video_shader_get_preset_extension(shader_types[j]);
- strlcpy(end, preset_ext, sizeof(preset_path) - (end - preset_path));
- if (path_is_valid(preset_path))
- return true;
- }
- }
- }
- break;
- }
- return false;
- }
- /**
- * menu_shader_manager_save_auto_preset:
- * @shader : shader to save
- * @type : type of shader preset which determines save path
- * @apply : immediately set preset after saving
- *
- * Save a shader as an auto-shader to it's appropriate path:
- * SHADER_PRESET_GLOBAL: <target dir>/global
- * SHADER_PRESET_CORE: <target dir>/<core name>/<core name>
- * SHADER_PRESET_PARENT: <target dir>/<core name>/<parent>
- * SHADER_PRESET_GAME: <target dir>/<core name>/<game name>
- * Needs to be consistent with retroarch_load_shader_preset()
- * Auto-shaders will be saved as a reference if possible
- **/
- bool menu_shader_manager_save_auto_preset(
- const struct video_shader *shader,
- enum auto_shader_type type,
- const char *dir_video_shader,
- const char *dir_menu_config,
- bool apply)
- {
- return menu_shader_manager_operate_auto_preset(
- AUTO_SHADER_OP_SAVE, shader,
- dir_video_shader,
- dir_menu_config,
- type, apply);
- }
- /**
- * menu_shader_manager_save_preset:
- * @shader : shader to save
- * @type : type of shader preset which determines save path
- * @basename : basename of preset
- * @apply : immediately set preset after saving
- *
- * Save a shader preset to disk.
- **/
- bool menu_shader_manager_save_preset(const struct video_shader *shader,
- const char *basename,
- const char *dir_video_shader,
- const char *dir_menu_config,
- bool apply)
- {
- char config_directory[PATH_MAX_LENGTH];
- const char *preset_dirs[3] = {0};
- config_directory[0] = '\0';
- if (!path_is_empty(RARCH_PATH_CONFIG))
- fill_pathname_basedir(
- config_directory,
- path_get(RARCH_PATH_CONFIG),
- sizeof(config_directory));
- preset_dirs[0] = dir_video_shader;
- preset_dirs[1] = dir_menu_config;
- preset_dirs[2] = config_directory;
- return menu_shader_manager_save_preset_internal(
- shader, basename,
- dir_video_shader,
- apply,
- preset_dirs,
- ARRAY_SIZE(preset_dirs));
- }
- /**
- * menu_shader_manager_remove_auto_preset:
- * @type : type of shader preset to delete
- *
- * Deletes an auto-shader.
- **/
- bool menu_shader_manager_remove_auto_preset(
- enum auto_shader_type type,
- const char *dir_video_shader,
- const char *dir_menu_config)
- {
- return menu_shader_manager_operate_auto_preset(
- AUTO_SHADER_OP_REMOVE, NULL,
- dir_video_shader,
- dir_menu_config,
- type, false);
- }
- /**
- * menu_shader_manager_auto_preset_exists:
- * @type : type of shader preset
- *
- * Tests if an auto-shader of the given type exists.
- **/
- bool menu_shader_manager_auto_preset_exists(
- enum auto_shader_type type,
- const char *dir_video_shader,
- const char *dir_menu_config)
- {
- return menu_shader_manager_operate_auto_preset(
- AUTO_SHADER_OP_EXISTS, NULL,
- dir_video_shader,
- dir_menu_config,
- type, false);
- }
- int menu_shader_manager_clear_num_passes(struct video_shader *shader)
- {
- bool refresh = false;
- if (!shader)
- return 0;
- shader->passes = 0;
- #ifdef HAVE_MENU
- menu_entries_ctl(MENU_ENTRIES_CTL_SET_REFRESH, &refresh);
- #endif
- video_shader_resolve_parameters(shader);
- shader->modified = true;
- return 0;
- }
- int menu_shader_manager_clear_parameter(struct video_shader *shader,
- unsigned i)
- {
- struct video_shader_parameter *param = shader ?
- &shader->parameters[i] : NULL;
- if (!param)
- return 0;
- param->current = param->initial;
- param->current = MIN(MAX(param->minimum,
- param->current), param->maximum);
- shader->modified = true;
- return 0;
- }
- int menu_shader_manager_clear_pass_filter(struct video_shader *shader,
- unsigned i)
- {
- struct video_shader_pass *shader_pass = shader ?
- &shader->pass[i] : NULL;
- if (!shader_pass)
- return -1;
- shader_pass->filter = RARCH_FILTER_UNSPEC;
- shader->modified = true;
- return 0;
- }
- void menu_shader_manager_clear_pass_scale(struct video_shader *shader,
- unsigned i)
- {
- struct video_shader_pass *shader_pass = shader ?
- &shader->pass[i] : NULL;
- if (!shader_pass)
- return;
- shader_pass->fbo.scale_x = 0;
- shader_pass->fbo.scale_y = 0;
- shader_pass->fbo.valid = false;
- shader->modified = true;
- }
- void menu_shader_manager_clear_pass_path(struct video_shader *shader,
- unsigned i)
- {
- struct video_shader_pass
- *shader_pass = shader
- ? &shader->pass[i]
- : NULL;
- if (shader_pass)
- *shader_pass->source.path = '\0';
- if (shader)
- shader->modified = true;
- }
- /**
- * menu_shader_manager_get_type:
- * @shader : shader handle
- *
- * Gets type of shader.
- *
- * Returns: type of shader.
- **/
- enum rarch_shader_type menu_shader_manager_get_type(
- const struct video_shader *shader)
- {
- enum rarch_shader_type type = RARCH_SHADER_NONE;
- /* All shader types must be the same, or we cannot use it. */
- size_t i = 0;
- if (!shader)
- return RARCH_SHADER_NONE;
- type = video_shader_parse_type(shader->path);
- if (!shader->passes)
- return type;
- if (type == RARCH_SHADER_NONE)
- {
- type = video_shader_parse_type(shader->pass[0].source.path);
- i = 1;
- }
- for (; i < shader->passes; i++)
- {
- enum rarch_shader_type pass_type =
- video_shader_parse_type(shader->pass[i].source.path);
- switch (pass_type)
- {
- case RARCH_SHADER_CG:
- case RARCH_SHADER_GLSL:
- case RARCH_SHADER_SLANG:
- if (type != pass_type)
- return RARCH_SHADER_NONE;
- break;
- default:
- break;
- }
- }
- return type;
- }
- /**
- * menu_shader_manager_apply_changes:
- *
- * Apply shader state changes.
- **/
- void menu_shader_manager_apply_changes(
- struct video_shader *shader,
- const char *dir_video_shader,
- const char *dir_menu_config)
- {
- enum rarch_shader_type type = RARCH_SHADER_NONE;
- if (!shader)
- return;
- type = menu_shader_manager_get_type(shader);
- if (shader->passes && type != RARCH_SHADER_NONE)
- {
- menu_shader_manager_save_preset(shader, NULL,
- dir_video_shader, dir_menu_config, true);
- return;
- }
- menu_shader_manager_set_preset(NULL, type, NULL, true);
- }
- #endif
- #ifdef HAVE_DISCORD
- bool discord_is_ready(void)
- {
- struct rarch_state *p_rarch = &rarch_st;
- discord_state_t *discord_st = &p_rarch->discord_st;
- return discord_st->ready;
- }
- static char *discord_get_own_username(struct rarch_state *p_rarch)
- {
- discord_state_t *discord_st = &p_rarch->discord_st;
- if (discord_st->ready)
- return discord_st->user_name;
- return NULL;
- }
- char *discord_get_own_avatar(void)
- {
- struct rarch_state *p_rarch = &rarch_st;
- discord_state_t *discord_st = &p_rarch->discord_st;
- if (discord_st->ready)
- return discord_st->user_avatar;
- return NULL;
- }
- bool discord_avatar_is_ready(void)
- {
- return false;
- }
- void discord_avatar_set_ready(bool ready)
- {
- struct rarch_state *p_rarch = &rarch_st;
- discord_state_t *discord_st = &p_rarch->discord_st;
- discord_st->avatar_ready = ready;
- }
- #ifdef HAVE_MENU
- static bool discord_download_avatar(
- const char* user_id, const char* avatar_id)
- {
- static char url[PATH_MAX_LENGTH];
- static char url_encoded[PATH_MAX_LENGTH];
- static char full_path[PATH_MAX_LENGTH];
- static char buf[PATH_MAX_LENGTH];
- file_transfer_t *transf = NULL;
- struct rarch_state *p_rarch = &rarch_st;
- discord_state_t *discord_st = &p_rarch->discord_st;
- RARCH_LOG("[DISCORD]: User avatar ID: %s\n", user_id);
- fill_pathname_application_special(buf,
- sizeof(buf),
- APPLICATION_SPECIAL_DIRECTORY_THUMBNAILS_DISCORD_AVATARS);
- fill_pathname_join(full_path, buf, avatar_id, sizeof(full_path));
- strlcpy(discord_st->user_avatar,
- avatar_id, sizeof(discord_st->user_avatar));
- if (path_is_valid(full_path))
- return true;
- if (string_is_empty(avatar_id))
- return false;
- snprintf(url, sizeof(url), "%s/%s/%s" FILE_PATH_PNG_EXTENSION, CDN_URL, user_id, avatar_id);
- net_http_urlencode_full(url_encoded, url, sizeof(url_encoded));
- snprintf(buf, sizeof(buf), "%s" FILE_PATH_PNG_EXTENSION, avatar_id);
- transf = (file_transfer_t*)malloc(sizeof(*transf));
- transf->enum_idx = MENU_ENUM_LABEL_CB_DISCORD_AVATAR;
- strlcpy(transf->path, buf, sizeof(transf->path));
- transf->user_data = NULL;
- RARCH_LOG("[DISCORD]: Downloading avatar from: %s\n", url_encoded);
- task_push_http_transfer_file(url_encoded, true, NULL, cb_generic_download, transf);
- return false;
- }
- #endif
- static void handle_discord_ready(const DiscordUser* connectedUser)
- {
- struct rarch_state *p_rarch = &rarch_st;
- discord_state_t *discord_st = &p_rarch->discord_st;
- strlcpy(discord_st->user_name,
- connectedUser->username, sizeof(discord_st->user_name));
- RARCH_LOG("[DISCORD]: Connected to user: %s#%s\n",
- connectedUser->username,
- connectedUser->discriminator);
- #ifdef HAVE_MENU
- discord_download_avatar(connectedUser->userId, connectedUser->avatar);
- #endif
- }
- static void handle_discord_disconnected(int errcode, const char* message)
- {
- RARCH_LOG("[DISCORD]: Disconnected (%d: %s)\n", errcode, message);
- }
- static void handle_discord_error(int errcode, const char* message)
- {
- RARCH_LOG("[DISCORD]: Error (%d: %s)\n", errcode, message);
- }
- static void handle_discord_join_cb(retro_task_t *task,
- void *task_data, void *user_data, const char *err)
- {
- char join_hostname[PATH_MAX_LENGTH];
- struct netplay_room *room = NULL;
- http_transfer_data_t *data = (http_transfer_data_t*)task_data;
- struct rarch_state *p_rarch = &rarch_st;
- discord_state_t *discord_st = &p_rarch->discord_st;
- if (!data || err || !data->data)
- goto finish;
- data->data = (char*)realloc(data->data, data->len + 1);
- data->data[data->len] = '\0';
- netplay_rooms_parse(data->data);
- room = netplay_room_get(0);
- if (room)
- {
- bool host_method_is_mitm = room->host_method == NETPLAY_HOST_METHOD_MITM;
- const char *srv_address = host_method_is_mitm ? room->mitm_address : room->address;
- unsigned srv_port = host_method_is_mitm ? room->mitm_port : room->port;
- if (netplay_driver_ctl(RARCH_NETPLAY_CTL_IS_DATA_INITED, NULL))
- deinit_netplay(p_rarch);
- netplay_driver_ctl(RARCH_NETPLAY_CTL_ENABLE_CLIENT, NULL);
- snprintf(join_hostname, sizeof(join_hostname), "%s|%d",
- srv_address, srv_port);
- RARCH_LOG("[DISCORD]: Joining lobby at: %s\n", join_hostname);
- task_push_netplay_crc_scan(room->gamecrc,
- room->gamename, join_hostname, room->corename, room->subsystem_name);
- discord_st->connecting = true;
- if (discord_st->ready)
- discord_update(DISCORD_PRESENCE_NETPLAY_CLIENT);
- }
- finish:
- if (err)
- RARCH_ERR("%s: %s\n", msg_hash_to_str(MSG_DOWNLOAD_FAILED), err);
- if (data)
- {
- if (data->data)
- free(data->data);
- free(data);
- }
- if (user_data)
- free(user_data);
- }
- static void handle_discord_join(const char* secret)
- {
- char url[2048] = FILE_PATH_LOBBY_LIBRETRO_URL;
- struct string_list *list = string_split(secret, "|");
- struct rarch_state *p_rarch = &rarch_st;
- discord_state_t *discord_st = &p_rarch->discord_st;
- strlcpy(discord_st->peer_party_id,
- list->elems[0].data, sizeof(discord_st->peer_party_id));
- strlcat(url, discord_st->peer_party_id, sizeof(url));
- strlcat(url, "/", sizeof(url));
- RARCH_LOG("[DISCORD]: Querying lobby id: %s at %s\n",
- discord_st->peer_party_id, url);
- task_push_http_transfer(url, true, NULL, handle_discord_join_cb, NULL);
- }
- static void handle_discord_spectate(const char* secret)
- {
- RARCH_LOG("[DISCORD]: Spectate (%s)\n", secret);
- }
- #ifdef HAVE_MENU
- #if 0
- static void handle_discord_join_response(void *ignore, const char *line)
- {
- /* TODO/FIXME: needs in-game widgets */
- if (strstr(line, "yes"))
- Discord_Respond(user_id, DISCORD_REPLY_YES);
- #ifdef HAVE_MENU
- menu_input_dialog_end();
- retroarch_menu_running_finished(false);
- #endif
- }
- #endif
- #endif
- static void handle_discord_join_request(const DiscordUser* request)
- {
- #ifdef HAVE_MENU
- #if 0
- char buf[PATH_MAX_LENGTH];
- #endif
- menu_input_ctx_line_t line;
- RARCH_LOG("[DISCORD]: Join request from %s#%s - %s %s\n",
- request->username,
- request->discriminator,
- request->userId,
- request->avatar);
- discord_download_avatar(request->userId, request->avatar);
- #if 0
- /* TODO/FIXME: Needs in-game widgets */
- retroarch_menu_running();
- memset(&line, 0, sizeof(line));
- snprintf(buf, sizeof(buf), "%s %s?",
- msg_hash_to_str(MSG_DISCORD_CONNECTION_REQUEST), request->username);
- line.label = buf;
- line.label_setting = "no_setting";
- line.cb = handle_discord_join_response;
- /* TODO/FIXME: needs in-game widgets
- * TODO/FIXME: bespoke dialog, should show while in-game
- * and have a hotkey to accept
- * TODO/FIXME: show avatar of the user connecting
- */
- if (!menu_input_dialog_start(&line))
- return;
- #endif
- #endif
- }
- void discord_update(enum discord_presence presence)
- {
- struct rarch_state *p_rarch = &rarch_st;
- discord_state_t *discord_st = &p_rarch->discord_st;
- if (presence == discord_st->status)
- return;
- if (!discord_st->connecting
- &&
- ( presence == DISCORD_PRESENCE_NONE
- || presence == DISCORD_PRESENCE_MENU))
- {
- memset(&discord_st->presence,
- 0, sizeof(discord_st->presence));
- discord_st->peer_party_id[0] = '\0';
- }
- switch (presence)
- {
- case DISCORD_PRESENCE_MENU:
- discord_st->presence.details = msg_hash_to_str(
- MENU_ENUM_LABEL_VALUE_DISCORD_IN_MENU);
- discord_st->presence.largeImageKey = "base";
- discord_st->presence.largeImageText = msg_hash_to_str(
- MENU_ENUM_LABEL_VALUE_NO_CORE);
- discord_st->presence.instance = 0;
- break;
- case DISCORD_PRESENCE_GAME_PAUSED:
- discord_st->presence.smallImageKey = "paused";
- discord_st->presence.smallImageText = msg_hash_to_str(
- MENU_ENUM_LABEL_VALUE_DISCORD_STATUS_PAUSED);
- discord_st->presence.details = msg_hash_to_str(
- MENU_ENUM_LABEL_VALUE_DISCORD_IN_GAME_PAUSED);
- discord_st->pause_time = time(0);
- discord_st->elapsed_time = difftime(discord_st->pause_time,
- discord_st->start_time);
- discord_st->presence.startTimestamp = discord_st->pause_time;
- break;
- case DISCORD_PRESENCE_GAME:
- {
- core_info_t *core_info = NULL;
- core_info_get_current_core(&core_info);
- if (core_info)
- {
- const char *system_id =
- core_info->system_id
- ? core_info->system_id
- : "core";
- const char *label = NULL;
- const struct playlist_entry *entry = NULL;
- playlist_t *current_playlist = playlist_get_cached();
- if (current_playlist)
- {
- playlist_get_index_by_path(
- current_playlist,
- path_get(RARCH_PATH_CONTENT),
- &entry);
- if (entry && !string_is_empty(entry->label))
- label = entry->label;
- }
- if (!label)
- label = path_basename(path_get(RARCH_PATH_BASENAME));
- discord_st->presence.largeImageKey = system_id;
- if (core_info->display_name)
- discord_st->presence.largeImageText =
- core_info->display_name;
- discord_st->start_time = time(0);
- if (discord_st->pause_time != 0)
- discord_st->start_time = time(0) -
- discord_st->elapsed_time;
- discord_st->pause_time = 0;
- discord_st->elapsed_time = 0;
- discord_st->presence.smallImageKey = "playing";
- discord_st->presence.smallImageText = msg_hash_to_str(
- MENU_ENUM_LABEL_VALUE_DISCORD_STATUS_PLAYING);
- discord_st->presence.startTimestamp = discord_st->start_time;
- #ifdef HAVE_CHEEVOS
- discord_st->presence.details = rcheevos_get_richpresence();
- if (!discord_st->presence.details || !*discord_st->presence.details)
- #endif
- discord_st->presence.details = msg_hash_to_str(
- MENU_ENUM_LABEL_VALUE_DISCORD_IN_GAME);
- discord_st->presence.state = label;
- discord_st->presence.instance = 0;
- if (!netplay_driver_ctl(RARCH_NETPLAY_CTL_IS_ENABLED, NULL))
- {
- discord_st->peer_party_id[0] = '\0';
- discord_st->connecting = false;
- discord_st->presence.partyId = NULL;
- discord_st->presence.partyMax = 0;
- discord_st->presence.partySize = 0;
- discord_st->presence.joinSecret = (const char*)'\0';
- }
- }
- }
- break;
- case DISCORD_PRESENCE_NETPLAY_HOSTING:
- {
- char join_secret[128];
- struct netplay_room *room = &p_rarch->netplay_host_room;
- bool host_method_is_mitm = room->host_method == NETPLAY_HOST_METHOD_MITM;
- const char *srv_address = host_method_is_mitm ? room->mitm_address : room->address;
- unsigned srv_port = host_method_is_mitm ? room->mitm_port : room->port;
- if (room->id == 0)
- return;
- RARCH_LOG("[DISCORD]: Netplay room details: ID=%d"
- ", Nick=%s IP=%s Port=%d\n",
- room->id, room->nickname,
- srv_address, srv_port);
- snprintf(discord_st->self_party_id,
- sizeof(discord_st->self_party_id), "%d", room->id);
- snprintf(join_secret,
- sizeof(join_secret), "%d|%" PRId64,
- room->id, cpu_features_get_time_usec());
- discord_st->presence.joinSecret = strdup(join_secret);
- #if 0
- discord_st->presence.spectateSecret = "SPECSPECSPEC";
- #endif
- discord_st->presence.partyId = strdup(discord_st->self_party_id);
- discord_st->presence.partyMax = 2;
- discord_st->presence.partySize = 1;
- RARCH_LOG("[DISCORD]: Join secret: %s\n", join_secret);
- RARCH_LOG("[DISCORD]: Party ID: %s\n", discord_st->self_party_id);
- }
- break;
- case DISCORD_PRESENCE_NETPLAY_CLIENT:
- RARCH_LOG("[DISCORD]: Party ID: %s\n", discord_st->peer_party_id);
- discord_st->presence.partyId = strdup(discord_st->peer_party_id);
- break;
- case DISCORD_PRESENCE_NETPLAY_NETPLAY_STOPPED:
- {
- if (!netplay_driver_ctl(RARCH_NETPLAY_CTL_IS_ENABLED, NULL) &&
- !netplay_driver_ctl(RARCH_NETPLAY_CTL_IS_CONNECTED, NULL))
- {
- discord_st->peer_party_id[0] = '\0';
- discord_st->connecting = false;
- discord_st->presence.partyId = NULL;
- discord_st->presence.partyMax = 0;
- discord_st->presence.partySize = 0;
- discord_st->presence.joinSecret = (const char*)'\0';
- }
- }
- break;
- #ifdef HAVE_CHEEVOS
- case DISCORD_PRESENCE_RETROACHIEVEMENTS:
- if (discord_st->pause_time)
- return;
- discord_st->presence.details = rcheevos_get_richpresence();
- presence = DISCORD_PRESENCE_GAME;
- break;
- #endif
- case DISCORD_PRESENCE_SHUTDOWN:
- discord_st->presence.partyId = NULL;
- discord_st->presence.partyMax = 0;
- discord_st->presence.partySize = 0;
- discord_st->presence.joinSecret = (const char*)'\0';
- discord_st->connecting = false;
- default:
- break;
- }
- #ifdef DEBUG
- RARCH_LOG("[DISCORD]: Updating (%d)\n", presence);
- #endif
- Discord_UpdatePresence(&discord_st->presence);
- Discord_UpdateConnection();
- discord_st->status = presence;
- }
- static void discord_init(
- discord_state_t *discord_st,
- const char *discord_app_id, char *args)
- {
- DiscordEventHandlers handlers;
- char full_path[PATH_MAX_LENGTH];
- char command[PATH_MAX_LENGTH];
- discord_st->start_time = time(0);
- handlers.ready = handle_discord_ready;
- handlers.disconnected = handle_discord_disconnected;
- handlers.errored = handle_discord_error;
- handlers.joinGame = handle_discord_join;
- handlers.spectateGame = handle_discord_spectate;
- handlers.joinRequest = handle_discord_join_request;
- Discord_Initialize(discord_app_id, &handlers, 0, NULL);
- Discord_UpdateConnection();
- #ifdef _WIN32
- fill_pathname_application_path(full_path, sizeof(full_path));
- if (strstr(args, full_path))
- strlcpy(command, args, sizeof(command));
- else
- {
- path_basedir(full_path);
- strlcpy(command, full_path, sizeof(command));
- strlcat(command, args, sizeof(command));
- }
- #else
- strcpy_literal(command, "sh -c ");
- strlcat(command, args, sizeof(command));
- #endif
- RARCH_LOG("[DISCORD]: Registering startup command: %s\n", command);
- Discord_Register(discord_app_id, command);
- Discord_UpdateConnection();
- discord_st->ready = true;
- }
- #endif
- #ifdef HAVE_NETWORKING
- /**
- * netplay_is_alive:
- * @netplay : pointer to netplay object
- *
- * Checks if input port/index is controlled by netplay or not.
- *
- * Returns: true (1) if alive, otherwise false (0).
- **/
- static bool netplay_is_alive(netplay_t *netplay)
- {
- return (netplay->is_server) ||
- (!netplay->is_server &&
- netplay->self_mode >= NETPLAY_CONNECTION_CONNECTED);
- }
- /**
- * netplay_should_skip:
- * @netplay : pointer to netplay object
- *
- * If we're fast-forward replaying to resync, check if we
- * should actually show frame.
- *
- * Returns: bool (1) if we should skip this frame, otherwise
- * false (0).
- **/
- static bool netplay_should_skip(netplay_t *netplay)
- {
- if (!netplay)
- return false;
- return netplay->is_replay
- && (netplay->self_mode >= NETPLAY_CONNECTION_CONNECTED);
- }
- /**
- * get_self_input_state:
- * @netplay : pointer to netplay object
- *
- * Grab our own input state and send this frame's input state (self and remote)
- * over the network
- *
- * Returns: true (1) if successful, otherwise false (0).
- */
- static bool get_self_input_state(
- bool block_libretro_input,
- netplay_t *netplay)
- {
- unsigned i;
- struct delta_frame *ptr = &netplay->buffer[netplay->self_ptr];
- netplay_input_state_t istate = NULL;
- uint32_t devices, used_devices = 0, devi, dev_type, local_device;
- if (!netplay_delta_frame_ready(netplay, ptr, netplay->self_frame_count))
- return false;
- /* We've already read this frame! */
- if (ptr->have_local)
- return true;
- devices = netplay->self_devices;
- used_devices = 0;
- for (devi = 0; devi < MAX_INPUT_DEVICES; devi++)
- {
- if (!(devices & (1 << devi)))
- continue;
- /* Find an appropriate local device */
- dev_type = netplay->config_devices[devi]&RETRO_DEVICE_MASK;
- for (local_device = 0; local_device < MAX_INPUT_DEVICES; local_device++)
- {
- if (used_devices & (1 << local_device))
- continue;
- if ((netplay->config_devices[local_device]&RETRO_DEVICE_MASK) == dev_type)
- break;
- }
- if (local_device == MAX_INPUT_DEVICES)
- local_device = 0;
- used_devices |= (1 << local_device);
- istate = netplay_input_state_for(&ptr->real_input[devi],
- /* If we're a slave, we write our own input to MAX_CLIENTS to keep it separate */
- (netplay->self_mode==NETPLAY_CONNECTION_SLAVE)?MAX_CLIENTS:netplay->self_client_num,
- netplay_expected_input_size(netplay, 1 << devi),
- true, false);
- if (!istate)
- continue; /* FIXME: More severe? */
- /* First frame we always give zero input since relying on
- * input from first frame screws up when we use -F 0. */
- if ( !block_libretro_input
- && netplay->self_frame_count > 0)
- {
- uint32_t *state = istate->data;
- retro_input_state_t cb = netplay->cbs.state_cb;
- unsigned dtype = netplay->config_devices[devi]&RETRO_DEVICE_MASK;
- switch (dtype)
- {
- case RETRO_DEVICE_ANALOG:
- for (i = 0; i < 2; i++)
- {
- int16_t tmp_x = cb(local_device,
- RETRO_DEVICE_ANALOG, (unsigned)i, 0);
- int16_t tmp_y = cb(local_device,
- RETRO_DEVICE_ANALOG, (unsigned)i, 1);
- state[1 + i] = (uint16_t)tmp_x | (((uint16_t)tmp_y) << 16);
- }
- /* no break */
- case RETRO_DEVICE_JOYPAD:
- for (i = 0; i <= RETRO_DEVICE_ID_JOYPAD_R3; i++)
- {
- int16_t tmp = cb(local_device,
- RETRO_DEVICE_JOYPAD, 0, (unsigned)i);
- state[0] |= tmp ? 1 << i : 0;
- }
- break;
- case RETRO_DEVICE_MOUSE:
- case RETRO_DEVICE_LIGHTGUN:
- {
- int16_t tmp_x = cb(local_device, dtype, 0, 0);
- int16_t tmp_y = cb(local_device, dtype, 0, 1);
- state[1] = (uint16_t)tmp_x | (((uint16_t)tmp_y) << 16);
- for (i = 2;
- i <= (unsigned)((dtype == RETRO_DEVICE_MOUSE) ?
- RETRO_DEVICE_ID_MOUSE_HORIZ_WHEELDOWN :
- RETRO_DEVICE_ID_LIGHTGUN_START);
- i++)
- {
- int16_t tmp = cb(local_device, dtype, 0,
- (unsigned) i);
- state[0] |= tmp ? 1 << i : 0;
- }
- break;
- }
- case RETRO_DEVICE_KEYBOARD:
- {
- unsigned key, word = 0, bit = 1;
- for (key = 1; key < NETPLAY_KEY_LAST; key++)
- {
- state[word] |=
- cb(local_device, RETRO_DEVICE_KEYBOARD, 0, netplay_key_ntoh(key)) ?
- (UINT32_C(1) << bit) : 0;
- bit++;
- if (bit >= 32)
- {
- bit = 0;
- word++;
- if (word >= istate->size)
- break;
- }
- }
- break;
- }
- }
- }
- }
- ptr->have_local = true;
- if (netplay->self_mode == NETPLAY_CONNECTION_PLAYING)
- {
- ptr->have_real[netplay->self_client_num] = true;
- netplay->read_ptr[netplay->self_client_num] = NEXT_PTR(netplay->self_ptr);
- netplay->read_frame_count[netplay->self_client_num] = netplay->self_frame_count + 1;
- }
- /* And send this input to our peers */
- for (i = 0; i < netplay->connections_size; i++)
- {
- struct netplay_connection *connection = &netplay->connections[i];
- if (connection->active && connection->mode >= NETPLAY_CONNECTION_CONNECTED)
- netplay_send_cur_input(netplay, &netplay->connections[i]);
- }
- /* Handle any delayed state changes */
- if (netplay->is_server)
- netplay_delayed_state_change(netplay);
- return true;
- }
- static bool init_netplay_deferred(
- struct rarch_state *p_rarch,
- const char* server, unsigned port)
- {
- if (!string_is_empty(server) && port != 0)
- {
- strlcpy(p_rarch->server_address_deferred, server,
- sizeof(p_rarch->server_address_deferred));
- p_rarch->server_port_deferred = port;
- p_rarch->netplay_client_deferred = true;
- }
- else
- p_rarch->netplay_client_deferred = false;
- return p_rarch->netplay_client_deferred;
- }
- /**
- * netplay_poll:
- * @netplay : pointer to netplay object
- *
- * Polls network to see if we have anything new. If our
- * network buffer is full, we simply have to block
- * for new input data.
- *
- * Returns: true (1) if successful, otherwise false (0).
- **/
- static bool netplay_poll(
- bool block_libretro_input,
- settings_t *settings,
- netplay_t *netplay)
- {
- int res;
- uint32_t client;
- size_t i;
- if (!get_self_input_state(block_libretro_input, netplay))
- goto catastrophe;
- /* If we're not connected, we're done */
- if (netplay->self_mode == NETPLAY_CONNECTION_NONE)
- return true;
- /* Read Netplay input, block if we're configured to stall for input every
- * frame */
- netplay_update_unread_ptr(netplay);
- if (netplay->stateless_mode &&
- (netplay->connected_players>1) &&
- netplay->unread_frame_count <= netplay->run_frame_count)
- res = netplay_poll_net_input(netplay, true);
- else
- res = netplay_poll_net_input(netplay, false);
- if (res == -1)
- goto catastrophe;
- /* Resolve and/or simulate the input if we don't have real input */
- netplay_resolve_input(netplay, netplay->run_ptr, false);
- /* Handle any slaves */
- if (netplay->is_server && netplay->connected_slaves)
- netplay_handle_slaves(netplay);
- netplay_update_unread_ptr(netplay);
- /* Figure out how many frames of input latency we should be using to hide
- * network latency */
- if (netplay->frame_run_time_avg || netplay->stateless_mode)
- {
- /* FIXME: Using fixed 60fps for this calculation */
- unsigned frames_per_frame = netplay->frame_run_time_avg ?
- (16666 / netplay->frame_run_time_avg) :
- 0;
- unsigned frames_ahead = (netplay->run_frame_count > netplay->unread_frame_count) ?
- (netplay->run_frame_count - netplay->unread_frame_count) :
- 0;
- int input_latency_frames_min = settings->uints.netplay_input_latency_frames_min -
- (settings->bools.run_ahead_enabled ? settings->uints.run_ahead_frames : 0);
- int input_latency_frames_max = input_latency_frames_min + settings->uints.netplay_input_latency_frames_range;
- /* Assume we need a couple frames worth of time to actually run the
- * current frame */
- if (frames_per_frame > 2)
- frames_per_frame -= 2;
- else
- frames_per_frame = 0;
- /* Shall we adjust our latency? */
- if (netplay->stateless_mode)
- {
- /* In stateless mode, we adjust up if we're "close" and down if we
- * have a lot of slack */
- if (netplay->input_latency_frames < input_latency_frames_min ||
- (netplay->unread_frame_count == netplay->run_frame_count + 1 &&
- netplay->input_latency_frames < input_latency_frames_max))
- netplay->input_latency_frames++;
- else if (netplay->input_latency_frames > input_latency_frames_max ||
- (netplay->unread_frame_count > netplay->run_frame_count + 2 &&
- netplay->input_latency_frames > input_latency_frames_min))
- netplay->input_latency_frames--;
- }
- else if (netplay->input_latency_frames < input_latency_frames_min ||
- (frames_per_frame < frames_ahead &&
- netplay->input_latency_frames < input_latency_frames_max))
- {
- /* We can't hide this much network latency with replay, so hide some
- * with input latency */
- netplay->input_latency_frames++;
- }
- else if (netplay->input_latency_frames > input_latency_frames_max ||
- (frames_per_frame > frames_ahead + 2 &&
- netplay->input_latency_frames > input_latency_frames_min))
- {
- /* We don't need this much latency (any more) */
- netplay->input_latency_frames--;
- }
- }
- /* If we're stalled, consider unstalling */
- switch (netplay->stall)
- {
- case NETPLAY_STALL_RUNNING_FAST:
- if (netplay->unread_frame_count + NETPLAY_MAX_STALL_FRAMES - 2
- > netplay->self_frame_count)
- {
- netplay->stall = NETPLAY_STALL_NONE;
- for (i = 0; i < netplay->connections_size; i++)
- {
- struct netplay_connection *connection = &netplay->connections[i];
- if (connection->active && connection->stall)
- connection->stall = NETPLAY_STALL_NONE;
- }
- }
- break;
- case NETPLAY_STALL_SPECTATOR_WAIT:
- if (netplay->self_mode == NETPLAY_CONNECTION_PLAYING || netplay->unread_frame_count > netplay->self_frame_count)
- netplay->stall = NETPLAY_STALL_NONE;
- break;
- case NETPLAY_STALL_INPUT_LATENCY:
- /* Just let it recalculate momentarily */
- netplay->stall = NETPLAY_STALL_NONE;
- break;
- case NETPLAY_STALL_SERVER_REQUESTED:
- /* See if the stall is done */
- if (netplay->connections[0].stall_frame == 0)
- {
- /* Stop stalling! */
- netplay->connections[0].stall = NETPLAY_STALL_NONE;
- netplay->stall = NETPLAY_STALL_NONE;
- }
- else
- netplay->connections[0].stall_frame--;
- break;
- case NETPLAY_STALL_NO_CONNECTION:
- /* We certainly haven't fixed this */
- break;
- default: /* not stalling */
- break;
- }
- /* If we're not stalled, consider stalling */
- if (!netplay->stall)
- {
- /* Have we not read enough latency frames? */
- if (netplay->self_mode == NETPLAY_CONNECTION_PLAYING &&
- netplay->connected_players &&
- netplay->run_frame_count + netplay->input_latency_frames > netplay->self_frame_count)
- {
- netplay->stall = NETPLAY_STALL_INPUT_LATENCY;
- netplay->stall_time = 0;
- }
- /* Are we too far ahead? */
- if (netplay->unread_frame_count + NETPLAY_MAX_STALL_FRAMES
- <= netplay->self_frame_count)
- {
- netplay->stall = NETPLAY_STALL_RUNNING_FAST;
- netplay->stall_time = cpu_features_get_time_usec();
- /* Figure out who to blame */
- if (netplay->is_server)
- {
- for (client = 1; client < MAX_CLIENTS; client++)
- {
- struct netplay_connection *connection;
- if (!(netplay->connected_players & (1 << client)))
- continue;
- if (netplay->read_frame_count[client] > netplay->unread_frame_count)
- continue;
- connection = &netplay->connections[client-1];
- if (connection->active &&
- connection->mode == NETPLAY_CONNECTION_PLAYING)
- {
- connection->stall = NETPLAY_STALL_RUNNING_FAST;
- connection->stall_time = netplay->stall_time;
- }
- }
- }
- }
- /* If we're a spectator, are we ahead at all? */
- if (!netplay->is_server &&
- (netplay->self_mode == NETPLAY_CONNECTION_SPECTATING ||
- netplay->self_mode == NETPLAY_CONNECTION_SLAVE) &&
- netplay->unread_frame_count <= netplay->self_frame_count)
- {
- netplay->stall = NETPLAY_STALL_SPECTATOR_WAIT;
- netplay->stall_time = cpu_features_get_time_usec();
- }
- }
- /* If we're stalling, consider disconnection */
- if (netplay->stall && netplay->stall_time)
- {
- retro_time_t now = cpu_features_get_time_usec();
- /* Don't stall out while they're paused */
- if (netplay->remote_paused)
- netplay->stall_time = now;
- else if (now - netplay->stall_time >=
- (netplay->is_server ? MAX_SERVER_STALL_TIME_USEC :
- MAX_CLIENT_STALL_TIME_USEC))
- {
- /* Stalled out! */
- if (netplay->is_server)
- {
- bool fixed = false;
- for (i = 0; i < netplay->connections_size; i++)
- {
- struct netplay_connection *connection = &netplay->connections[i];
- if (connection->active &&
- connection->mode == NETPLAY_CONNECTION_PLAYING &&
- connection->stall)
- {
- netplay_hangup(netplay, connection);
- fixed = true;
- }
- }
- if (fixed)
- {
- /* Not stalled now :) */
- netplay->stall = NETPLAY_STALL_NONE;
- return true;
- }
- }
- else
- goto catastrophe;
- return false;
- }
- }
- return true;
- catastrophe:
- for (i = 0; i < netplay->connections_size; i++)
- netplay_hangup(netplay, &netplay->connections[i]);
- return false;
- }
- /**
- * input_poll_net
- *
- * Poll the network if necessary.
- */
- void input_poll_net(void)
- {
- struct rarch_state *p_rarch = &rarch_st;
- netplay_t *netplay = p_rarch->netplay_data;
- if (!netplay_should_skip(netplay) && netplay && netplay->can_poll)
- {
- netplay->can_poll = false;
- netplay_poll(
- p_rarch->input_driver_block_libretro_input,
- p_rarch->configuration_settings,
- netplay);
- }
- }
- /* Netplay polling callbacks */
- static void video_frame_net(const void *data, unsigned width,
- unsigned height, size_t pitch)
- {
- struct rarch_state *p_rarch = &rarch_st;
- netplay_t *netplay = p_rarch->netplay_data;
- if (!netplay_should_skip(netplay))
- netplay->cbs.frame_cb(data, width, height, pitch);
- }
- static void audio_sample_net(int16_t left, int16_t right)
- {
- struct rarch_state *p_rarch = &rarch_st;
- netplay_t *netplay = p_rarch->netplay_data;
- if (!netplay_should_skip(netplay) && !netplay->stall)
- netplay->cbs.sample_cb(left, right);
- }
- static size_t audio_sample_batch_net(const int16_t *data, size_t frames)
- {
- struct rarch_state *p_rarch = &rarch_st;
- netplay_t *netplay = p_rarch->netplay_data;
- if (!netplay_should_skip(netplay) && !netplay->stall)
- return netplay->cbs.sample_batch_cb(data, frames);
- return frames;
- }
- static int16_t netplay_input_state(netplay_t *netplay,
- unsigned port, unsigned device,
- unsigned idx, unsigned id)
- {
- struct delta_frame *delta;
- netplay_input_state_t istate;
- const uint32_t *curr_input_state = NULL;
- size_t ptr =
- netplay->is_replay
- ? netplay->replay_ptr
- : netplay->run_ptr;
- if (port >= MAX_INPUT_DEVICES)
- return 0;
- /* If the port doesn't seem to correspond to the device, "correct" it. This
- * is common with devices that typically only have one instance, such as
- * keyboards, mice and lightguns. */
- if (device != RETRO_DEVICE_JOYPAD &&
- (netplay->config_devices[port]&RETRO_DEVICE_MASK) != device)
- {
- for (port = 0; port < MAX_INPUT_DEVICES; port++)
- {
- if ((netplay->config_devices[port]&RETRO_DEVICE_MASK) == device)
- break;
- }
- if (port == MAX_INPUT_DEVICES)
- return 0;
- }
- delta = &netplay->buffer[ptr];
- istate = delta->resolved_input[port];
- if (!istate || !istate->used || istate->size == 0)
- return 0;
- curr_input_state = istate->data;
- switch (device)
- {
- case RETRO_DEVICE_JOYPAD:
- if (id == RETRO_DEVICE_ID_JOYPAD_MASK)
- return curr_input_state[0];
- return ((1 << id) & curr_input_state[0]) ? 1 : 0;
- case RETRO_DEVICE_ANALOG:
- if (istate->size == 3)
- {
- uint32_t state = curr_input_state[1 + idx];
- return (int16_t)(uint16_t)(state >> (id * 16));
- }
- break;
- case RETRO_DEVICE_MOUSE:
- case RETRO_DEVICE_LIGHTGUN:
- if (istate->size == 2)
- {
- if (id <= RETRO_DEVICE_ID_MOUSE_Y)
- return (int16_t)(uint16_t)(curr_input_state[1] >> (id * 16));
- return ((1 << id) & curr_input_state[0]) ? 1 : 0;
- }
- break;
- case RETRO_DEVICE_KEYBOARD:
- {
- unsigned key = netplay_key_hton(id);
- if (key != NETPLAY_KEY_UNKNOWN)
- {
- unsigned word = key / 32;
- unsigned bit = key % 32;
- if (word <= istate->size)
- return ((UINT32_C(1) << bit) & curr_input_state[word]) ? 1 : 0;
- }
- }
- break;
- default:
- break;
- }
- return 0;
- }
- static void netplay_announce_cb(retro_task_t *task,
- void *task_data, void *user_data, const char *error)
- {
- if (task_data)
- {
- unsigned i, ip_len, port_len;
- struct rarch_state *p_rarch = &rarch_st;
- http_transfer_data_t *data = (http_transfer_data_t*)task_data;
- struct netplay_room *host_room = &p_rarch->netplay_host_room;
- struct string_list *lines = NULL;
- char *mitm_ip = NULL;
- char *mitm_port = NULL;
- char *buf = NULL;
- char *host_string = NULL;
- if (data->len == 0)
- {
- free(task_data);
- return;
- }
- buf = (char*)calloc(1, data->len + 1);
- memcpy(buf, data->data, data->len);
- lines = string_split(buf, "\n");
- if (lines->size == 0)
- {
- string_list_free(lines);
- free(buf);
- free(task_data);
- return;
- }
- memset(host_room, 0, sizeof(*host_room));
- for (i = 0; i < lines->size; i++)
- {
- const char *line = lines->elems[i].data;
- if (!string_is_empty(line))
- {
- struct string_list *kv = string_split(line, "=");
- const char *key = NULL;
- const char *val = NULL;
- if (!kv)
- continue;
- if (kv->size != 2)
- {
- string_list_free(kv);
- continue;
- }
- key = kv->elems[0].data;
- val = kv->elems[1].data;
- if (string_is_equal(key, "id"))
- sscanf(val, "%i", &host_room->id);
- if (string_is_equal(key, "username"))
- strlcpy(host_room->nickname, val, sizeof(host_room->nickname));
- if (string_is_equal(key, "ip"))
- strlcpy(host_room->address, val, sizeof(host_room->address));
- if (string_is_equal(key, "mitm_ip"))
- {
- mitm_ip = strdup(val);
- strlcpy(host_room->mitm_address, val, sizeof(host_room->mitm_address));
- }
- if (string_is_equal(key, "port"))
- sscanf(val, "%i", &host_room->port);
- if (string_is_equal(key, "mitm_port"))
- {
- mitm_port = strdup(val);
- sscanf(mitm_port, "%i", &host_room->mitm_port);
- }
- if (string_is_equal(key, "core_name"))
- strlcpy(host_room->corename, val, sizeof(host_room->corename));
- if (string_is_equal(key, "frontend"))
- strlcpy(host_room->frontend, val, sizeof(host_room->frontend));
- if (string_is_equal(key, "core_version"))
- strlcpy(host_room->coreversion, val, sizeof(host_room->coreversion));
- if (string_is_equal(key, "game_name"))
- strlcpy(host_room->gamename, val, sizeof(host_room->gamename));
- if (string_is_equal(key, "game_crc"))
- sscanf(val, "%08d", &host_room->gamecrc);
- if (string_is_equal(key, "host_method"))
- sscanf(val, "%i", &host_room->host_method);
- if (string_is_equal(key, "has_password"))
- {
- if (string_is_equal_noncase(val, "true") || string_is_equal(val, "1"))
- host_room->has_password = true;
- else
- host_room->has_password = false;
- }
- if (string_is_equal(key, "has_spectate_password"))
- {
- if (string_is_equal_noncase(val, "true") || string_is_equal(val, "1"))
- host_room->has_spectate_password = true;
- else
- host_room->has_spectate_password = false;
- }
- if (string_is_equal(key, "fixed"))
- {
- if (string_is_equal_noncase(val, "true") || string_is_equal(val, "1"))
- host_room->fixed = true;
- else
- host_room->fixed = false;
- }
- if (string_is_equal(key, "retroarch_version"))
- strlcpy(host_room->retroarch_version, val, sizeof(host_room->retroarch_version));
- if (string_is_equal(key, "country"))
- strlcpy(host_room->country, val, sizeof(host_room->country));
- string_list_free(kv);
- }
- }
- if (mitm_ip && mitm_port)
- {
- ip_len = (unsigned)strlen(mitm_ip);
- port_len = (unsigned)strlen(mitm_port);
- /* Enable Netplay client mode */
- if (netplay_driver_ctl(RARCH_NETPLAY_CTL_IS_DATA_INITED, NULL))
- {
- command_event(CMD_EVENT_NETPLAY_DEINIT, NULL);
- p_rarch->is_mitm = true;
- host_room->host_method = NETPLAY_HOST_METHOD_MITM;
- }
- netplay_driver_ctl(RARCH_NETPLAY_CTL_ENABLE_CLIENT, NULL);
- host_string = (char*)calloc(1, ip_len + port_len + 2);
- memcpy(host_string, mitm_ip, ip_len);
- memcpy(host_string + ip_len, "|", 1);
- memcpy(host_string + ip_len + 1, mitm_port, port_len);
- /* Enable Netplay */
- command_event(CMD_EVENT_NETPLAY_INIT_DIRECT_DEFERRED, (void*)host_string);
- command_event(CMD_EVENT_NETPLAY_INIT, (void*)host_string);
- free(host_string);
- }
- #ifdef HAVE_DISCORD
- if (discord_is_inited)
- {
- discord_userdata_t userdata;
- userdata.status = DISCORD_PRESENCE_NETPLAY_HOSTING;
- command_event(CMD_EVENT_DISCORD_UPDATE, &userdata);
- }
- #endif
- string_list_free(lines);
- free(buf);
- free(task_data);
- if (mitm_ip)
- free(mitm_ip);
- if (mitm_port)
- free(mitm_port);
- }
- }
- static void netplay_announce(struct rarch_state *p_rarch)
- {
- char buf[4600];
- char frontend_architecture[PATH_MAX_LENGTH];
- char frontend_architecture_tmp[32];
- const frontend_ctx_driver_t
- *frontend_drv = NULL;
- char url[2048] = "http://lobby.libretro.com/add/";
- char *username = NULL;
- char *corename = NULL;
- char *gamename = NULL;
- char *subsystemname = NULL;
- char *coreversion = NULL;
- char *frontend_ident = NULL;
- settings_t *settings = p_rarch->configuration_settings;
- struct retro_system_info *system = &p_rarch->runloop_system.info;
- uint32_t content_crc = content_get_crc();
- struct string_list *subsystem = path_get_subsystem_list();
- buf[0] = '\0';
- if (subsystem)
- {
- unsigned i;
- for (i = 0; i < subsystem->size; i++)
- {
- strlcat(buf, path_basename(subsystem->elems[i].data), sizeof(buf));
- if (i < subsystem->size - 1)
- strlcat(buf, "|", sizeof(buf));
- }
- net_http_urlencode(&gamename, buf);
- net_http_urlencode(&subsystemname, path_get(RARCH_PATH_SUBSYSTEM));
- content_crc = 0;
- }
- else
- {
- net_http_urlencode(&gamename,
- !string_is_empty(path_basename(path_get(RARCH_PATH_BASENAME))) ?
- path_basename(path_get(RARCH_PATH_BASENAME)) : "N/A");
- net_http_urlencode(&subsystemname, "N/A");
- }
- frontend_drv =
- (const frontend_ctx_driver_t*)frontend_driver_get_cpu_architecture_str(
- frontend_architecture_tmp, sizeof(frontend_architecture_tmp));
- strlcpy(frontend_architecture, frontend_drv->ident,
- sizeof(frontend_architecture));
- strlcat(frontend_architecture, " ",
- sizeof(frontend_architecture));
- strlcat(frontend_architecture, frontend_architecture_tmp,
- sizeof(frontend_architecture));
- #ifdef HAVE_DISCORD
- if (discord_is_ready())
- net_http_urlencode(&username, discord_get_own_username(p_rarch));
- else
- #endif
- net_http_urlencode(&username, settings->paths.username);
- net_http_urlencode(&corename, system->library_name);
- net_http_urlencode(&coreversion, system->library_version);
- net_http_urlencode(&frontend_ident, frontend_architecture);
- buf[0] = '\0';
- snprintf(buf, sizeof(buf), "username=%s&core_name=%s&core_version=%s&"
- "game_name=%s&game_crc=%08X&port=%d&mitm_server=%s"
- "&has_password=%d&has_spectate_password=%d&force_mitm=%d"
- "&retroarch_version=%s&frontend=%s&subsystem_name=%s",
- username, corename, coreversion, gamename, content_crc,
- settings->uints.netplay_port,
- settings->arrays.netplay_mitm_server,
- *settings->paths.netplay_password ? 1 : 0,
- *settings->paths.netplay_spectate_password ? 1 : 0,
- settings->bools.netplay_use_mitm_server,
- PACKAGE_VERSION, frontend_architecture, subsystemname);
- task_push_http_post_transfer(url, buf, true, NULL,
- netplay_announce_cb, NULL);
- if (username)
- free(username);
- if (corename)
- free(corename);
- if (gamename)
- free(gamename);
- if (coreversion)
- free(coreversion);
- if (frontend_ident)
- free(frontend_ident);
- }
- static int16_t input_state_net(unsigned port, unsigned device,
- unsigned idx, unsigned id)
- {
- struct rarch_state *p_rarch = &rarch_st;
- netplay_t *netplay = p_rarch->netplay_data;
- if (netplay)
- {
- if (netplay_is_alive(netplay))
- return netplay_input_state(netplay, port, device, idx, id);
- return netplay->cbs.state_cb(port, device, idx, id);
- }
- return 0;
- }
- /* ^^^ Netplay polling callbacks */
- /**
- * netplay_frontend_paused
- * @netplay : pointer to netplay object
- * @paused : true if frontend is paused
- *
- * Inform Netplay of the frontend's pause state (paused or otherwise)
- */
- static void netplay_frontend_paused(netplay_t *netplay, bool paused)
- {
- size_t i;
- uint32_t paused_ct = 0;
- netplay->local_paused = paused;
- /* Communicating this is a bit odd: If exactly one other connection is
- * paused, then we must tell them that we're unpaused, as from their
- * perspective we are. If more than one other connection is paused, then our
- * status as proxy means we are NOT unpaused to either of them. */
- for (i = 0; i < netplay->connections_size; i++)
- {
- struct netplay_connection *connection = &netplay->connections[i];
- if (connection->active && connection->paused)
- paused_ct++;
- }
- if (paused_ct > 1)
- return;
- /* Send our unpaused status. Must send manually because we must immediately
- * flush the buffer: If we're paused, we won't be polled. */
- for (i = 0; i < netplay->connections_size; i++)
- {
- struct netplay_connection *connection = &netplay->connections[i];
- if ( connection->active
- && connection->mode >= NETPLAY_CONNECTION_CONNECTED)
- {
- if (paused)
- netplay_send_raw_cmd(netplay, connection, NETPLAY_CMD_PAUSE,
- netplay->nick, NETPLAY_NICK_LEN);
- else
- netplay_send_raw_cmd(netplay, connection, NETPLAY_CMD_RESUME,
- NULL, 0);
- /* We're not going to be polled, so we need to
- * flush this command now */
- netplay_send_flush(&connection->send_packet_buffer,
- connection->fd, true);
- }
- }
- }
- /**
- * netplay_disconnect
- * @netplay : pointer to netplay object
- *
- * Disconnect netplay.
- *
- * Returns: true (1) if successful. At present, cannot fail.
- **/
- static void netplay_disconnect(
- struct rarch_state *p_rarch,
- netplay_t *netplay)
- {
- size_t i;
- for (i = 0; i < netplay->connections_size; i++)
- netplay_hangup(netplay, &netplay->connections[i]);
- deinit_netplay(p_rarch);
- #ifdef HAVE_DISCORD
- if (discord_is_inited)
- {
- discord_userdata_t userdata;
- userdata.status = DISCORD_PRESENCE_NETPLAY_NETPLAY_STOPPED;
- command_event(CMD_EVENT_DISCORD_UPDATE, &userdata);
- }
- #endif
- }
- /**
- * netplay_pre_frame:
- * @netplay : pointer to netplay object
- *
- * Pre-frame for Netplay.
- * Call this before running retro_run().
- *
- * Returns: true (1) if the frontend is cleared to emulate the frame, false (0)
- * if we're stalled or paused
- **/
- static bool netplay_pre_frame(struct rarch_state *p_rarch,
- netplay_t *netplay)
- {
- bool sync_stalled = false;
- settings_t *settings = p_rarch->configuration_settings;
- retro_assert(netplay);
- if (settings->bools.netplay_public_announce)
- {
- p_rarch->reannounce++;
- if (
- (netplay->is_server || p_rarch->is_mitm) &&
- (p_rarch->reannounce % 300 == 0))
- netplay_announce(p_rarch);
- }
- /* Make sure that if announcement is turned on mid-game, it gets announced */
- else
- p_rarch->reannounce = -1;
- /* FIXME: This is an ugly way to learn we're not paused anymore */
- if (netplay->local_paused)
- if (netplay->local_paused != false)
- netplay_frontend_paused(netplay, false);
- /* Are we ready now? */
- if (netplay->quirks & NETPLAY_QUIRK_INITIALIZATION)
- netplay_try_init_serialization(netplay);
- if (netplay->is_server && !settings->bools.netplay_use_mitm_server)
- {
- /* Advertise our server */
- netplay_lan_ad_server(netplay);
- /* NAT traversal if applicable */
- if (netplay->nat_traversal &&
- !netplay->nat_traversal_task_oustanding &&
- netplay->nat_traversal_state.request_outstanding &&
- !netplay->nat_traversal_state.have_inet4)
- {
- struct timeval tmptv = {0};
- fd_set fds = netplay->nat_traversal_state.fds;
- if (socket_select(netplay->nat_traversal_state.nfds, &fds, NULL, NULL, &tmptv) > 0)
- natt_read(&netplay->nat_traversal_state);
- #ifndef HAVE_SOCKET_LEGACY
- if (!netplay->nat_traversal_state.request_outstanding ||
- netplay->nat_traversal_state.have_inet4)
- netplay_announce_nat_traversal(netplay);
- #endif
- }
- }
- sync_stalled = !netplay_sync_pre_frame(netplay);
- /* If we're disconnected, deinitialize */
- if (!netplay->is_server && !netplay->connections[0].active)
- {
- netplay_disconnect(p_rarch, netplay);
- return true;
- }
- if (sync_stalled ||
- ((!netplay->is_server || (netplay->connected_players>1)) &&
- (netplay->stall || netplay->remote_paused)))
- {
- /* We may have received data even if we're stalled, so run post-frame
- * sync */
- netplay_sync_post_frame(netplay, true);
- return false;
- }
- return true;
- }
- /**
- * netplay_post_frame:
- * @netplay : pointer to netplay object
- *
- * Post-frame for Netplay.
- * We check if we have new input and replay from recorded input.
- * Call this after running retro_run().
- **/
- static void netplay_post_frame(
- struct rarch_state *p_rarch,
- netplay_t *netplay)
- {
- size_t i;
- retro_assert(netplay);
- netplay_update_unread_ptr(netplay);
- netplay_sync_post_frame(netplay, false);
- for (i = 0; i < netplay->connections_size; i++)
- {
- struct netplay_connection *connection = &netplay->connections[i];
- if (connection->active &&
- !netplay_send_flush(&connection->send_packet_buffer, connection->fd,
- false))
- netplay_hangup(netplay, connection);
- }
- /* If we're disconnected, deinitialize */
- if (!netplay->is_server && !netplay->connections[0].active)
- netplay_disconnect(p_rarch, netplay);
- }
- /**
- * netplay_force_future
- * @netplay : pointer to netplay object
- *
- * Force netplay to ignore all past input, typically because we've just loaded
- * a state or reset.
- */
- static void netplay_force_future(netplay_t *netplay)
- {
- /* Wherever we're inputting, that's where we consider our state to be loaded */
- netplay->run_ptr = netplay->self_ptr;
- netplay->run_frame_count = netplay->self_frame_count;
- /* We need to ignore any intervening data from the other side,
- * and never rewind past this */
- netplay_update_unread_ptr(netplay);
- if (netplay->unread_frame_count < netplay->run_frame_count)
- {
- uint32_t client;
- for (client = 0; client < MAX_CLIENTS; client++)
- {
- if (!(netplay->connected_players & (1 << client)))
- continue;
- if (netplay->read_frame_count[client] < netplay->run_frame_count)
- {
- netplay->read_ptr[client] = netplay->run_ptr;
- netplay->read_frame_count[client] = netplay->run_frame_count;
- }
- }
- if (netplay->server_frame_count < netplay->run_frame_count)
- {
- netplay->server_ptr = netplay->run_ptr;
- netplay->server_frame_count = netplay->run_frame_count;
- }
- netplay_update_unread_ptr(netplay);
- }
- if (netplay->other_frame_count < netplay->run_frame_count)
- {
- netplay->other_ptr = netplay->run_ptr;
- netplay->other_frame_count = netplay->run_frame_count;
- }
- }
- /**
- * netplay_send_savestate
- * @netplay : pointer to netplay object
- * @serial_info : the savestate being loaded
- * @cx : compression type
- * @z : compression backend to use
- *
- * Send a loaded savestate to those connected peers using the given compression
- * scheme.
- */
- static void netplay_send_savestate(netplay_t *netplay,
- retro_ctx_serialize_info_t *serial_info, uint32_t cx,
- struct compression_transcoder *z)
- {
- uint32_t header[4];
- uint32_t rd, wn;
- size_t i;
- /* Compress it */
- z->compression_backend->set_in(z->compression_stream,
- (const uint8_t*)serial_info->data_const, (uint32_t)serial_info->size);
- z->compression_backend->set_out(z->compression_stream,
- netplay->zbuffer, (uint32_t)netplay->zbuffer_size);
- if (!z->compression_backend->trans(z->compression_stream, true, &rd,
- &wn, NULL))
- {
- /* Catastrophe! */
- for (i = 0; i < netplay->connections_size; i++)
- netplay_hangup(netplay, &netplay->connections[i]);
- return;
- }
- /* Send it to relevant peers */
- header[0] = htonl(NETPLAY_CMD_LOAD_SAVESTATE);
- header[1] = htonl(wn + 2*sizeof(uint32_t));
- header[2] = htonl(netplay->run_frame_count);
- header[3] = htonl(serial_info->size);
- for (i = 0; i < netplay->connections_size; i++)
- {
- struct netplay_connection *connection = &netplay->connections[i];
- if (!connection->active ||
- connection->mode < NETPLAY_CONNECTION_CONNECTED ||
- connection->compression_supported != cx) continue;
- if (!netplay_send(&connection->send_packet_buffer, connection->fd, header,
- sizeof(header)) ||
- !netplay_send(&connection->send_packet_buffer, connection->fd,
- netplay->zbuffer, wn))
- netplay_hangup(netplay, connection);
- }
- }
- /**
- * netplay_load_savestate
- * @netplay : pointer to netplay object
- * @serial_info : the savestate being loaded, NULL means
- * "load it yourself"
- * @save : Whether to save the provided serial_info
- * into the frame buffer
- *
- * Inform Netplay of a savestate load and send it to the other side
- **/
- void netplay_load_savestate(netplay_t *netplay,
- retro_ctx_serialize_info_t *serial_info, bool save)
- {
- retro_ctx_serialize_info_t tmp_serial_info;
- netplay_force_future(netplay);
- /* Record it in our own buffer */
- if (save || !serial_info)
- {
- if (netplay_delta_frame_ready(netplay,
- &netplay->buffer[netplay->run_ptr], netplay->run_frame_count))
- {
- if (!serial_info)
- {
- tmp_serial_info.size = netplay->state_size;
- tmp_serial_info.data = netplay->buffer[netplay->run_ptr].state;
- if (!core_serialize(&tmp_serial_info))
- return;
- tmp_serial_info.data_const = tmp_serial_info.data;
- serial_info = &tmp_serial_info;
- }
- else
- {
- if (serial_info->size <= netplay->state_size)
- memcpy(netplay->buffer[netplay->run_ptr].state,
- serial_info->data_const, serial_info->size);
- }
- }
- /* FIXME: This is a critical failure! */
- else
- return;
- }
- /* Don't send it if we're expected to be desynced */
- if (netplay->desync)
- return;
- /* If we can't send it to the peer, loading a state was a bad idea */
- if (netplay->quirks & (
- NETPLAY_QUIRK_NO_SAVESTATES
- | NETPLAY_QUIRK_NO_TRANSMISSION))
- return;
- /* Send this to every peer */
- if (netplay->compress_nil.compression_backend)
- netplay_send_savestate(netplay, serial_info, 0, &netplay->compress_nil);
- if (netplay->compress_zlib.compression_backend)
- netplay_send_savestate(netplay, serial_info, NETPLAY_COMPRESSION_ZLIB,
- &netplay->compress_zlib);
- }
- /**
- * netplay_core_reset
- * @netplay : pointer to netplay object
- *
- * Indicate that the core has been reset to netplay peers
- **/
- static void netplay_core_reset(netplay_t *netplay)
- {
- size_t i;
- uint32_t cmd[3];
- /* Ignore past input */
- netplay_force_future(netplay);
- /* Request that our peers reset */
- cmd[0] = htonl(NETPLAY_CMD_RESET);
- cmd[1] = htonl(sizeof(uint32_t));
- cmd[2] = htonl(netplay->self_frame_count);
- for (i = 0; i < netplay->connections_size; i++)
- {
- struct netplay_connection *connection = &netplay->connections[i];
- if (!connection->active ||
- connection->mode < NETPLAY_CONNECTION_CONNECTED) continue;
- if (!netplay_send(&connection->send_packet_buffer, connection->fd, cmd,
- sizeof(cmd)))
- netplay_hangup(netplay, connection);
- }
- }
- /**
- * netplay_settings_share_mode
- *
- * Get the preferred share mode
- */
- uint8_t netplay_settings_share_mode(
- unsigned share_digital, unsigned share_analog)
- {
- if (share_digital || share_analog)
- {
- uint8_t share_mode = 0;
- switch (share_digital)
- {
- case RARCH_NETPLAY_SHARE_DIGITAL_OR:
- share_mode |= NETPLAY_SHARE_DIGITAL_OR;
- break;
- case RARCH_NETPLAY_SHARE_DIGITAL_XOR:
- share_mode |= NETPLAY_SHARE_DIGITAL_XOR;
- break;
- case RARCH_NETPLAY_SHARE_DIGITAL_VOTE:
- share_mode |= NETPLAY_SHARE_DIGITAL_VOTE;
- break;
- default:
- share_mode |= NETPLAY_SHARE_NO_PREFERENCE;
- }
- switch (share_analog)
- {
- case RARCH_NETPLAY_SHARE_ANALOG_MAX:
- share_mode |= NETPLAY_SHARE_ANALOG_MAX;
- break;
- case RARCH_NETPLAY_SHARE_ANALOG_AVERAGE:
- share_mode |= NETPLAY_SHARE_ANALOG_AVERAGE;
- break;
- default:
- share_mode |= NETPLAY_SHARE_NO_PREFERENCE;
- }
- return share_mode;
- }
- return 0;
- }
- /**
- * netplay_toggle_play_spectate
- *
- * Toggle between play mode and spectate mode
- */
- static void netplay_toggle_play_spectate(netplay_t *netplay)
- {
- switch (netplay->self_mode)
- {
- case NETPLAY_CONNECTION_PLAYING:
- case NETPLAY_CONNECTION_SLAVE:
- /* Switch to spectator mode immediately */
- netplay->self_mode = NETPLAY_CONNECTION_SPECTATING;
- netplay_cmd_mode(netplay, NETPLAY_CONNECTION_SPECTATING);
- break;
- case NETPLAY_CONNECTION_SPECTATING:
- /* Switch only after getting permission */
- netplay_cmd_mode(netplay, NETPLAY_CONNECTION_PLAYING);
- break;
- default:
- break;
- }
- }
- static void deinit_netplay(struct rarch_state *p_rarch)
- {
- if (p_rarch->netplay_data)
- {
- netplay_free(p_rarch->netplay_data);
- p_rarch->netplay_enabled = false;
- p_rarch->netplay_is_client = false;
- p_rarch->is_mitm = false;
- }
- p_rarch->netplay_data = NULL;
- core_unset_netplay_callbacks();
- }
- /**
- * init_netplay
- * @direct_host : Host to connect to directly, if applicable (client only)
- * @server : server address to connect to (client only)
- * @port : TCP port to host on/connect to
- *
- * Initializes netplay.
- *
- * If netplay is already initialized, will return false (0).
- *
- * Returns: true (1) if successful, otherwise false (0).
- **/
- static bool init_netplay(
- struct rarch_state *p_rarch,
- void *direct_host,
- const char *server, unsigned port)
- {
- struct retro_callbacks cbs = {0};
- settings_t *settings = p_rarch->configuration_settings;
- uint64_t serialization_quirks = 0;
- uint64_t quirks = 0;
- bool _netplay_is_client = p_rarch->netplay_is_client;
- bool _netplay_enabled = p_rarch->netplay_enabled;
- if (!_netplay_enabled)
- return false;
- core_set_default_callbacks(&cbs);
- if (!core_set_netplay_callbacks())
- return false;
- /* Map the core's quirks to our quirks */
- serialization_quirks = core_serialization_quirks();
- /* Quirks we don't support! Just disable everything. */
- if (serialization_quirks & ~((uint64_t) NETPLAY_QUIRK_MAP_UNDERSTOOD))
- quirks |= NETPLAY_QUIRK_NO_SAVESTATES;
- if (serialization_quirks & NETPLAY_QUIRK_MAP_NO_SAVESTATES)
- quirks |= NETPLAY_QUIRK_NO_SAVESTATES;
- if (serialization_quirks & NETPLAY_QUIRK_MAP_NO_TRANSMISSION)
- quirks |= NETPLAY_QUIRK_NO_TRANSMISSION;
- if (serialization_quirks & NETPLAY_QUIRK_MAP_INITIALIZATION)
- quirks |= NETPLAY_QUIRK_INITIALIZATION;
- if (serialization_quirks & NETPLAY_QUIRK_MAP_ENDIAN_DEPENDENT)
- quirks |= NETPLAY_QUIRK_ENDIAN_DEPENDENT;
- if (serialization_quirks & NETPLAY_QUIRK_MAP_PLATFORM_DEPENDENT)
- quirks |= NETPLAY_QUIRK_PLATFORM_DEPENDENT;
- if (_netplay_is_client)
- {
- RARCH_LOG("[Netplay]: %s\n", msg_hash_to_str(MSG_CONNECTING_TO_NETPLAY_HOST));
- }
- else
- {
- RARCH_LOG("[Netplay]: %s\n", msg_hash_to_str(MSG_WAITING_FOR_CLIENT));
- runloop_msg_queue_push(
- msg_hash_to_str(MSG_WAITING_FOR_CLIENT),
- 0, 180, false,
- NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO);
- if (settings->bools.netplay_public_announce)
- netplay_announce(p_rarch);
- }
- p_rarch->netplay_data = (netplay_t*)netplay_new(
- _netplay_is_client
- ? direct_host
- : NULL,
- _netplay_is_client
- ? (!p_rarch->netplay_client_deferred
- ? server
- : p_rarch->server_address_deferred)
- : NULL,
- _netplay_is_client ? (!p_rarch->netplay_client_deferred
- ? port
- : p_rarch->server_port_deferred)
- : (port != 0 ? port : RARCH_DEFAULT_PORT),
- settings->bools.netplay_stateless_mode,
- settings->ints.netplay_check_frames,
- &cbs,
- settings->bools.netplay_nat_traversal && !settings->bools.netplay_use_mitm_server,
- #ifdef HAVE_DISCORD
- discord_get_own_username(p_rarch)
- ? discord_get_own_username(p_rarch)
- :
- #endif
- settings->paths.username,
- quirks);
- if (p_rarch->netplay_data)
- {
- if ( p_rarch->netplay_data->is_server
- && !settings->bools.netplay_start_as_spectator)
- netplay_toggle_play_spectate(p_rarch->netplay_data);
- return true;
- }
- RARCH_WARN("%s\n", msg_hash_to_str(MSG_NETPLAY_FAILED));
- runloop_msg_queue_push(
- msg_hash_to_str(MSG_NETPLAY_FAILED),
- 0, 180, false,
- NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO);
- return false;
- }
- /**
- * netplay_driver_ctl
- *
- * Frontend access to Netplay functionality
- */
- bool netplay_driver_ctl(enum rarch_netplay_ctl_state state, void *data)
- {
- struct rarch_state *p_rarch = &rarch_st;
- netplay_t *netplay = p_rarch->netplay_data;
- bool ret = true;
- if (p_rarch->in_netplay)
- return true;
- p_rarch->in_netplay = true;
- if (!netplay)
- {
- switch (state)
- {
- case RARCH_NETPLAY_CTL_ENABLE_SERVER:
- p_rarch->netplay_enabled = true;
- p_rarch->netplay_is_client = false;
- goto done;
- case RARCH_NETPLAY_CTL_ENABLE_CLIENT:
- p_rarch->netplay_enabled = true;
- p_rarch->netplay_is_client = true;
- break;
- case RARCH_NETPLAY_CTL_DISABLE:
- p_rarch->netplay_enabled = false;
- #ifdef HAVE_DISCORD
- if (discord_is_inited)
- {
- discord_userdata_t userdata;
- userdata.status = DISCORD_PRESENCE_NETPLAY_NETPLAY_STOPPED;
- command_event(CMD_EVENT_DISCORD_UPDATE, &userdata);
- }
- #endif
- goto done;
- case RARCH_NETPLAY_CTL_IS_ENABLED:
- ret = p_rarch->netplay_enabled;
- goto done;
- case RARCH_NETPLAY_CTL_IS_REPLAYING:
- case RARCH_NETPLAY_CTL_IS_DATA_INITED:
- ret = false;
- goto done;
- case RARCH_NETPLAY_CTL_IS_SERVER:
- ret = p_rarch->netplay_enabled
- && !p_rarch->netplay_is_client;
- goto done;
- case RARCH_NETPLAY_CTL_IS_CONNECTED:
- ret = false;
- goto done;
- default:
- goto done;
- }
- }
- switch (state)
- {
- case RARCH_NETPLAY_CTL_ENABLE_SERVER:
- case RARCH_NETPLAY_CTL_ENABLE_CLIENT:
- case RARCH_NETPLAY_CTL_IS_DATA_INITED:
- goto done;
- case RARCH_NETPLAY_CTL_DISABLE:
- ret = false;
- goto done;
- case RARCH_NETPLAY_CTL_IS_ENABLED:
- goto done;
- case RARCH_NETPLAY_CTL_IS_REPLAYING:
- ret = netplay->is_replay;
- goto done;
- case RARCH_NETPLAY_CTL_IS_SERVER:
- ret = p_rarch->netplay_enabled
- && !p_rarch->netplay_is_client;
- goto done;
- case RARCH_NETPLAY_CTL_IS_CONNECTED:
- ret = netplay->is_connected;
- goto done;
- case RARCH_NETPLAY_CTL_POST_FRAME:
- netplay_post_frame(p_rarch, netplay);
- break;
- case RARCH_NETPLAY_CTL_PRE_FRAME:
- ret = netplay_pre_frame(p_rarch, netplay);
- goto done;
- case RARCH_NETPLAY_CTL_GAME_WATCH:
- netplay_toggle_play_spectate(netplay);
- break;
- case RARCH_NETPLAY_CTL_PAUSE:
- if (netplay->local_paused != true)
- netplay_frontend_paused(netplay, true);
- break;
- case RARCH_NETPLAY_CTL_UNPAUSE:
- if (netplay->local_paused != false)
- netplay_frontend_paused(netplay, false);
- break;
- case RARCH_NETPLAY_CTL_LOAD_SAVESTATE:
- netplay_load_savestate(netplay, (retro_ctx_serialize_info_t*)data, true);
- break;
- case RARCH_NETPLAY_CTL_RESET:
- netplay_core_reset(netplay);
- break;
- case RARCH_NETPLAY_CTL_DISCONNECT:
- ret = true;
- if (netplay)
- netplay_disconnect(p_rarch, netplay);
- goto done;
- case RARCH_NETPLAY_CTL_FINISHED_NAT_TRAVERSAL:
- netplay->nat_traversal_task_oustanding = false;
- #ifndef HAVE_SOCKET_LEGACY
- netplay_announce_nat_traversal(netplay);
- #endif
- goto done;
- case RARCH_NETPLAY_CTL_DESYNC_PUSH:
- netplay->desync++;
- break;
- case RARCH_NETPLAY_CTL_DESYNC_POP:
- if (netplay->desync)
- {
- netplay->desync--;
- if (!netplay->desync)
- netplay_load_savestate(netplay, NULL, true);
- }
- break;
- default:
- case RARCH_NETPLAY_CTL_NONE:
- ret = false;
- }
- done:
- p_rarch->in_netplay = false;
- return ret;
- }
- #endif
- static void log_counters(
- struct retro_perf_counter **counters, unsigned num)
- {
- unsigned i;
- for (i = 0; i < num; i++)
- {
- if (counters[i]->call_cnt)
- {
- RARCH_LOG(PERF_LOG_FMT,
- counters[i]->ident,
- (uint64_t)counters[i]->total /
- (uint64_t)counters[i]->call_cnt,
- (uint64_t)counters[i]->call_cnt);
- }
- }
- }
- static void retro_perf_log(void)
- {
- struct rarch_state *p_rarch = &rarch_st;
- RARCH_LOG("[PERF]: Performance counters (libretro):\n");
- log_counters(p_rarch->perf_counters_libretro, p_rarch->perf_ptr_libretro);
- }
- struct retro_perf_counter **retro_get_perf_counter_rarch(void)
- {
- struct rarch_state *p_rarch = &rarch_st;
- return p_rarch->perf_counters_rarch;
- }
- struct retro_perf_counter **retro_get_perf_counter_libretro(void)
- {
- struct rarch_state *p_rarch = &rarch_st;
- return p_rarch->perf_counters_libretro;
- }
- unsigned retro_get_perf_count_rarch(void)
- {
- struct rarch_state *p_rarch = &rarch_st;
- return p_rarch->perf_ptr_rarch;
- }
- unsigned retro_get_perf_count_libretro(void)
- {
- struct rarch_state *p_rarch = &rarch_st;
- return p_rarch->perf_ptr_libretro;
- }
- void rarch_perf_register(struct retro_perf_counter *perf)
- {
- struct rarch_state *p_rarch = &rarch_st;
- if (
- !p_rarch->runloop_perfcnt_enable
- || perf->registered
- || p_rarch->perf_ptr_rarch >= MAX_COUNTERS
- )
- return;
- p_rarch->perf_counters_rarch[p_rarch->perf_ptr_rarch++] = perf;
- perf->registered = true;
- }
- static void performance_counter_register(struct retro_perf_counter *perf)
- {
- struct rarch_state *p_rarch = &rarch_st;
- if (perf->registered || p_rarch->perf_ptr_libretro >= MAX_COUNTERS)
- return;
- p_rarch->perf_counters_libretro[p_rarch->perf_ptr_libretro++] = perf;
- perf->registered = true;
- }
- struct string_list *dir_list_new_special(const char *input_dir,
- enum dir_list_type type, const char *filter,
- bool show_hidden_files)
- {
- #if defined(HAVE_CG) || defined(HAVE_GLSL) || defined(HAVE_SLANG) || defined(HAVE_HLSL)
- char ext_shaders[255];
- #endif
- char ext_name[255];
- const char *exts = NULL;
- bool recursive = false;
- switch (type)
- {
- case DIR_LIST_AUTOCONFIG:
- exts = filter;
- break;
- case DIR_LIST_CORES:
- ext_name[0] = '\0';
- if (!frontend_driver_get_core_extension(ext_name, sizeof(ext_name)))
- return NULL;
- exts = ext_name;
- break;
- case DIR_LIST_RECURSIVE:
- recursive = true;
- /* fall-through */
- case DIR_LIST_CORE_INFO:
- {
- core_info_list_t *list = NULL;
- core_info_get_list(&list);
- if (list)
- exts = list->all_ext;
- }
- break;
- case DIR_LIST_SHADERS:
- #if defined(HAVE_CG) || defined(HAVE_GLSL) || defined(HAVE_SLANG) || defined(HAVE_HLSL)
- {
- union string_list_elem_attr attr;
- struct string_list str_list;
- if (!string_list_initialize(&str_list))
- return NULL;
- ext_shaders[0] = '\0';
- attr.i = 0;
- if (video_shader_is_supported(RARCH_SHADER_CG))
- {
- string_list_append(&str_list, "cgp", attr);
- string_list_append(&str_list, "cg", attr);
- }
- if (video_shader_is_supported(RARCH_SHADER_GLSL))
- {
- string_list_append(&str_list, "glslp", attr);
- string_list_append(&str_list, "glsl", attr);
- }
- if (video_shader_is_supported(RARCH_SHADER_SLANG))
- {
- string_list_append(&str_list, "slangp", attr);
- string_list_append(&str_list, "slang", attr);
- }
- string_list_join_concat(ext_shaders, sizeof(ext_shaders), &str_list, "|");
- string_list_deinitialize(&str_list);
- exts = ext_shaders;
- }
- break;
- #else
- return NULL;
- #endif
- case DIR_LIST_COLLECTIONS:
- exts = "lpl";
- break;
- case DIR_LIST_DATABASES:
- exts = "rdb";
- break;
- case DIR_LIST_PLAIN:
- exts = filter;
- break;
- case DIR_LIST_NONE:
- default:
- return NULL;
- }
- return dir_list_new(input_dir, exts, false,
- show_hidden_files,
- type == DIR_LIST_CORE_INFO, recursive);
- }
- struct string_list *string_list_new_special(enum string_list_type type,
- void *data, unsigned *len, size_t *list_size)
- {
- union string_list_elem_attr attr;
- unsigned i;
- core_info_list_t *core_info_list = NULL;
- const core_info_t *core_info = NULL;
- struct string_list *s = string_list_new();
- if (!s || !len)
- goto error;
- attr.i = 0;
- *len = 0;
- switch (type)
- {
- case STRING_LIST_MENU_DRIVERS:
- #ifdef HAVE_MENU
- for (i = 0; menu_ctx_drivers[i]; i++)
- {
- const char *opt = menu_ctx_drivers[i]->ident;
- *len += strlen(opt) + 1;
- /* Don't allow the user to set menu driver to "null" using the UI.
- * Can prevent the user from locking him/herself out of the program. */
- if (string_is_not_equal(opt, "null"))
- string_list_append(s, opt, attr);
- }
- break;
- #endif
- case STRING_LIST_CAMERA_DRIVERS:
- for (i = 0; camera_drivers[i]; i++)
- {
- const char *opt = camera_drivers[i]->ident;
- *len += strlen(opt) + 1;
- string_list_append(s, opt, attr);
- }
- break;
- case STRING_LIST_BLUETOOTH_DRIVERS:
- #ifdef HAVE_BLUETOOTH
- for (i = 0; bluetooth_drivers[i]; i++)
- {
- const char *opt = bluetooth_drivers[i]->ident;
- *len += strlen(opt) + 1;
- string_list_append(s, opt, attr);
- }
- break;
- #endif
- case STRING_LIST_WIFI_DRIVERS:
- #ifdef HAVE_WIFI
- for (i = 0; wifi_drivers[i]; i++)
- {
- const char *opt = wifi_drivers[i]->ident;
- *len += strlen(opt) + 1;
- string_list_append(s, opt, attr);
- }
- break;
- #endif
- case STRING_LIST_LOCATION_DRIVERS:
- for (i = 0; location_drivers[i]; i++)
- {
- const char *opt = location_drivers[i]->ident;
- *len += strlen(opt) + 1;
- string_list_append(s, opt, attr);
- }
- break;
- case STRING_LIST_AUDIO_DRIVERS:
- for (i = 0; audio_drivers[i]; i++)
- {
- const char *opt = audio_drivers[i]->ident;
- *len += strlen(opt) + 1;
- string_list_append(s, opt, attr);
- }
- break;
- case STRING_LIST_AUDIO_RESAMPLER_DRIVERS:
- for (i = 0; audio_resampler_driver_find_handle(i); i++)
- {
- const char *opt = audio_resampler_driver_find_ident(i);
- *len += strlen(opt) + 1;
- string_list_append(s, opt, attr);
- }
- break;
- case STRING_LIST_VIDEO_DRIVERS:
- for (i = 0; video_drivers[i]; i++)
- {
- const char *opt = video_drivers[i]->ident;
- *len += strlen(opt) + 1;
- /* Don't allow the user to set video driver to "null" using the UI.
- * Can prevent the user from locking him/herself out of the program. */
- if (string_is_not_equal(opt, "null"))
- string_list_append(s, opt, attr);
- }
- break;
- case STRING_LIST_INPUT_DRIVERS:
- for (i = 0; input_drivers[i]; i++)
- {
- const char *opt = input_drivers[i]->ident;
- *len += strlen(opt) + 1;
- /* Don't allow the user to set input driver to "null" using the UI.
- * Can prevent the user from locking him/herself out of the program. */
- if (string_is_not_equal(opt, "null"))
- string_list_append(s, opt, attr);
- }
- break;
- case STRING_LIST_INPUT_HID_DRIVERS:
- #ifdef HAVE_HID
- for (i = 0; hid_drivers[i]; i++)
- {
- const char *opt = hid_drivers[i]->ident;
- *len += strlen(opt) + 1;
- /* Don't allow the user to set input HID driver to "null" using the UI.
- * Can prevent the user from locking him/herself out of the program. */
- if (string_is_not_equal(opt, "null"))
- string_list_append(s, opt, attr);
- }
- #endif
- break;
- case STRING_LIST_INPUT_JOYPAD_DRIVERS:
- for (i = 0; joypad_drivers[i]; i++)
- {
- const char *opt = joypad_drivers[i]->ident;
- *len += strlen(opt) + 1;
- /* Don't allow the user to set input joypad driver to "null" using the UI.
- * Can prevent the user from locking him/herself out of the program. */
- if (string_is_not_equal(opt, "null"))
- string_list_append(s, opt, attr);
- }
- break;
- case STRING_LIST_RECORD_DRIVERS:
- for (i = 0; record_drivers[i]; i++)
- {
- const char *opt = record_drivers[i]->ident;
- *len += strlen(opt) + 1;
- string_list_append(s, opt, attr);
- }
- break;
- case STRING_LIST_MIDI_DRIVERS:
- for (i = 0; midi_driver_find_handle(i); i++)
- {
- const char *opt = midi_drivers[i]->ident;
- *len += strlen(opt) + 1;
- string_list_append(s, opt, attr);
- }
- break;
- case STRING_LIST_SUPPORTED_CORES_PATHS:
- core_info_get_list(&core_info_list);
- core_info_list_get_supported_cores(core_info_list,
- (const char*)data, &core_info, list_size);
- if (!core_info)
- goto error;
- if (*list_size == 0)
- goto error;
- for (i = 0; i < *list_size; i++)
- {
- const core_info_t *info = (const core_info_t*)&core_info[i];
- const char *opt = info->path;
- if (!opt)
- goto error;
- *len += strlen(opt) + 1;
- string_list_append(s, opt, attr);
- }
- break;
- case STRING_LIST_SUPPORTED_CORES_NAMES:
- core_info_get_list(&core_info_list);
- core_info_list_get_supported_cores(core_info_list,
- (const char*)data, &core_info, list_size);
- if (!core_info)
- goto error;
- if (*list_size == 0)
- goto error;
- for (i = 0; i < *list_size; i++)
- {
- core_info_t *info = (core_info_t*)&core_info[i];
- const char *opt = info->display_name;
- if (!opt)
- goto error;
- *len += strlen(opt) + 1;
- string_list_append(s, opt, attr);
- }
- break;
- case STRING_LIST_NONE:
- default:
- goto error;
- }
- return s;
- error:
- string_list_free(s);
- s = NULL;
- return NULL;
- }
- const char *char_list_new_special(enum string_list_type type, void *data)
- {
- unsigned len = 0;
- size_t list_size;
- struct string_list *s = string_list_new_special(type, data, &len, &list_size);
- char *options = (len > 0) ? (char*)calloc(len, sizeof(char)): NULL;
- if (options && s)
- string_list_join_concat(options, len, s, "|");
- string_list_free(s);
- s = NULL;
- return options;
- }
- static void path_set_redirect(struct rarch_state *p_rarch)
- {
- char content_dir_name[PATH_MAX_LENGTH];
- char new_savefile_dir[PATH_MAX_LENGTH];
- char new_savestate_dir[PATH_MAX_LENGTH];
- global_t *global = &p_rarch->g_extern;
- const char *old_savefile_dir = p_rarch->dir_savefile;
- const char *old_savestate_dir = p_rarch->dir_savestate;
- struct retro_system_info *system = &p_rarch->runloop_system.info;
- settings_t *settings = p_rarch->configuration_settings;
- bool sort_savefiles_enable = settings->bools.sort_savefiles_enable;
- bool sort_savefiles_by_content_enable = settings->bools.sort_savefiles_by_content_enable;
- bool sort_savestates_enable = settings->bools.sort_savestates_enable;
- bool sort_savestates_by_content_enable = settings->bools.sort_savestates_by_content_enable;
- bool savefiles_in_content_dir = settings->bools.savefiles_in_content_dir;
- bool savestates_in_content_dir = settings->bools.savestates_in_content_dir;
- content_dir_name[0] = '\0';
- new_savefile_dir[0] = '\0';
- new_savestate_dir[0] = '\0';
- /* Initialize current save directories
- * with the values from the config. */
- strlcpy(new_savefile_dir, old_savefile_dir, sizeof(new_savefile_dir));
- strlcpy(new_savestate_dir, old_savestate_dir, sizeof(new_savestate_dir));
- /* Get content directory name, if per-content-directory
- * saves/states are enabled */
- if ((sort_savefiles_by_content_enable ||
- sort_savestates_by_content_enable) &&
- !string_is_empty(p_rarch->path_main_basename))
- fill_pathname_parent_dir_name(content_dir_name,
- p_rarch->path_main_basename, sizeof(content_dir_name));
- if (system && !string_is_empty(system->library_name))
- {
- #ifdef HAVE_MENU
- if (!string_is_equal(system->library_name,
- msg_hash_to_str(MENU_ENUM_LABEL_VALUE_NO_CORE)))
- #endif
- {
- /* Per-core and/or per-content-directory saves */
- if ((sort_savefiles_enable || sort_savefiles_by_content_enable)
- && !string_is_empty(old_savefile_dir))
- {
- /* Append content directory name to save location */
- if (sort_savefiles_by_content_enable)
- fill_pathname_join(
- new_savefile_dir,
- old_savefile_dir,
- content_dir_name,
- sizeof(new_savefile_dir));
- /* Append library_name to the save location */
- if (sort_savefiles_enable)
- fill_pathname_join(
- new_savefile_dir,
- new_savefile_dir,
- system->library_name,
- sizeof(new_savefile_dir));
- /* If path doesn't exist, try to create it,
- * if everything fails revert to the original path. */
- if (!path_is_directory(new_savefile_dir))
- if (!path_mkdir(new_savefile_dir))
- {
- RARCH_LOG("%s %s\n",
- msg_hash_to_str(MSG_REVERTING_SAVEFILE_DIRECTORY_TO),
- old_savefile_dir);
- strlcpy(new_savefile_dir, old_savefile_dir, sizeof(new_savefile_dir));
- }
- }
- /* Per-core and/or per-content-directory savestates */
- if ((sort_savestates_enable || sort_savestates_by_content_enable)
- && !string_is_empty(old_savestate_dir))
- {
- /* Append content directory name to savestate location */
- if (sort_savestates_by_content_enable)
- fill_pathname_join(
- new_savestate_dir,
- old_savestate_dir,
- content_dir_name,
- sizeof(new_savestate_dir));
- /* Append library_name to the savestate location */
- if (sort_savestates_enable)
- {
- fill_pathname_join(
- new_savestate_dir,
- new_savestate_dir,
- system->library_name,
- sizeof(new_savestate_dir));
- }
-
- /* If path doesn't exist, try to create it.
- * If everything fails, revert to the original path. */
- if (!path_is_directory(new_savestate_dir))
- if (!path_mkdir(new_savestate_dir))
- {
- RARCH_LOG("%s %s\n",
- msg_hash_to_str(MSG_REVERTING_SAVESTATE_DIRECTORY_TO),
- old_savestate_dir);
- strlcpy(new_savestate_dir,
- old_savestate_dir,
- sizeof(new_savestate_dir));
- }
- }
- }
- }
- /* Set savefile directory if empty to content directory */
- if (string_is_empty(new_savefile_dir) || savefiles_in_content_dir)
- {
- strlcpy(new_savefile_dir, p_rarch->path_main_basename,
- sizeof(new_savefile_dir));
- path_basedir(new_savefile_dir);
- if (string_is_empty(new_savefile_dir))
- RARCH_LOG("Cannot resolve save file path.\n",
- msg_hash_to_str(MSG_REVERTING_SAVEFILE_DIRECTORY_TO),
- new_savefile_dir);
- else if (sort_savefiles_enable || sort_savefiles_by_content_enable)
- RARCH_LOG("Saving files in content directory is set. This overrides other save file directory settings.\n");
- }
- /* Set savestate directory if empty based on content directory */
- if (string_is_empty(new_savestate_dir) || savestates_in_content_dir)
- {
- strlcpy(new_savestate_dir, p_rarch->path_main_basename,
- sizeof(new_savestate_dir));
- path_basedir(new_savestate_dir);
- if (string_is_empty(new_savestate_dir))
- RARCH_LOG("Cannot resolve save state file path.\n",
- msg_hash_to_str(MSG_REVERTING_SAVESTATE_DIRECTORY_TO),
- new_savestate_dir);
- else if (sort_savestates_enable || sort_savestates_by_content_enable)
- RARCH_LOG("Saving save states in content directory is set. This overrides other save state file directory settings.\n");
- }
- if (global && system && !string_is_empty(system->library_name))
- {
- bool savefile_is_dir = path_is_directory(new_savefile_dir);
- bool savestate_is_dir = path_is_directory(new_savestate_dir);
- if (savefile_is_dir)
- strlcpy(global->name.savefile, new_savefile_dir,
- sizeof(global->name.savefile));
- else
- savefile_is_dir = path_is_directory(global->name.savefile);
- if (savestate_is_dir)
- strlcpy(global->name.savestate, new_savestate_dir,
- sizeof(global->name.savestate));
- else
- savestate_is_dir = path_is_directory(global->name.savestate);
- if (savefile_is_dir)
- {
- fill_pathname_dir(global->name.savefile,
- !string_is_empty(p_rarch->path_main_basename)
- ? p_rarch->path_main_basename
- : system->library_name,
- FILE_PATH_SRM_EXTENSION,
- sizeof(global->name.savefile));
- RARCH_LOG("[Overrides]: %s \"%s\".\n",
- msg_hash_to_str(MSG_REDIRECTING_SAVEFILE_TO),
- global->name.savefile);
- }
- if (savestate_is_dir)
- {
- fill_pathname_dir(global->name.savestate,
- !string_is_empty(p_rarch->path_main_basename)
- ? p_rarch->path_main_basename
- : system->library_name,
- FILE_PATH_STATE_EXTENSION,
- sizeof(global->name.savestate));
- RARCH_LOG("[Overrides]: %s \"%s\".\n",
- msg_hash_to_str(MSG_REDIRECTING_SAVESTATE_TO),
- global->name.savestate);
- }
- #ifdef HAVE_CHEATS
- if (path_is_directory(global->name.cheatfile))
- {
- fill_pathname_dir(global->name.cheatfile,
- !string_is_empty(p_rarch->path_main_basename)
- ? p_rarch->path_main_basename
- : system->library_name,
- FILE_PATH_CHT_EXTENSION,
- sizeof(global->name.cheatfile));
- RARCH_LOG("[Overrides]: %s \"%s\".\n",
- msg_hash_to_str(MSG_REDIRECTING_CHEATFILE_TO),
- global->name.cheatfile);
- }
- #endif
- }
- dir_set(RARCH_DIR_CURRENT_SAVEFILE, new_savefile_dir);
- dir_set(RARCH_DIR_CURRENT_SAVESTATE, new_savestate_dir);
- }
- static void path_set_basename(
- struct rarch_state *p_rarch,
- const char *path)
- {
- char *dst = NULL;
- path_set(RARCH_PATH_CONTENT, path);
- path_set(RARCH_PATH_BASENAME, path);
- #ifdef HAVE_COMPRESSION
- /* Removing extension is a bit tricky for compressed files.
- * Basename means:
- * /file/to/path/game.extension should be:
- * /file/to/path/game
- *
- * Two things to consider here are: /file/to/path/ is expected
- * to be a directory and "game" is a single file. This is used for
- * states and srm default paths.
- *
- * For compressed files we have:
- *
- * /file/to/path/comp.7z#game.extension and
- * /file/to/path/comp.7z#folder/game.extension
- *
- * The choice I take here is:
- * /file/to/path/game as basename. We might end up in a writable
- * directory then and the name of srm and states are meaningful.
- *
- */
- path_basedir_wrapper(p_rarch->path_main_basename);
- if (!string_is_empty(p_rarch->path_main_basename))
- fill_pathname_dir(p_rarch->path_main_basename, path, "", sizeof(p_rarch->path_main_basename));
- #endif
- if ((dst = strrchr(p_rarch->path_main_basename, '.')))
- *dst = '\0';
- }
- struct string_list *path_get_subsystem_list(void)
- {
- struct rarch_state *p_rarch = &rarch_st;
- return p_rarch->subsystem_fullpaths;
- }
- void path_set_special(char **argv, unsigned num_content)
- {
- unsigned i;
- char str[PATH_MAX_LENGTH];
- union string_list_elem_attr attr;
- struct string_list subsystem_paths = {0};
- struct rarch_state *p_rarch = &rarch_st;
- global_t *global = &p_rarch->g_extern;
- const char *savestate_dir = p_rarch->current_savestate_dir;
- /* First content file is the significant one. */
- path_set_basename(p_rarch, argv[0]);
- string_list_initialize(&subsystem_paths);
- p_rarch->subsystem_fullpaths = string_list_new();
- retro_assert(p_rarch->subsystem_fullpaths);
- attr.i = 0;
- for (i = 0; i < num_content; i++)
- {
- string_list_append(p_rarch->subsystem_fullpaths, argv[i], attr);
- strlcpy(str, argv[i], sizeof(str));
- path_remove_extension(str);
- string_list_append(&subsystem_paths, path_basename(str), attr);
- }
- str[0] = '\0';
- string_list_join_concat(str, sizeof(str), &subsystem_paths, " + ");
- string_list_deinitialize(&subsystem_paths);
- /* We defer SRAM path updates until we can resolve it.
- * It is more complicated for special content types. */
- if (global)
- {
- bool is_dir = path_is_directory(savestate_dir);
- if (is_dir)
- strlcpy(global->name.savestate, savestate_dir,
- sizeof(global->name.savestate));
- else
- is_dir = path_is_directory(global->name.savestate);
- if (is_dir)
- {
- fill_pathname_dir(global->name.savestate,
- str,
- ".state",
- sizeof(global->name.savestate));
- RARCH_LOG("%s \"%s\".\n",
- msg_hash_to_str(MSG_REDIRECTING_SAVESTATE_TO),
- global->name.savestate);
- }
- }
- }
- static bool path_init_subsystem(struct rarch_state *p_rarch)
- {
- unsigned i, j;
- const struct retro_subsystem_info *info = NULL;
- global_t *global = &p_rarch->g_extern;
- rarch_system_info_t *system = &p_rarch->runloop_system;
- bool subsystem_path_empty = path_is_empty(RARCH_PATH_SUBSYSTEM);
- const char *savefile_dir = p_rarch->current_savefile_dir;
- if (!system || subsystem_path_empty)
- return false;
- /* For subsystems, we know exactly which RAM types are supported. */
- info = libretro_find_subsystem_info(
- system->subsystem.data,
- system->subsystem.size,
- path_get(RARCH_PATH_SUBSYSTEM));
- /* We'll handle this error gracefully later. */
- if (info)
- {
- unsigned num_content = MIN(info->num_roms,
- subsystem_path_empty ?
- 0 : (unsigned)p_rarch->subsystem_fullpaths->size);
- for (i = 0; i < num_content; i++)
- {
- for (j = 0; j < info->roms[i].num_memory; j++)
- {
- char ext[32];
- union string_list_elem_attr attr;
- char savename[PATH_MAX_LENGTH];
- char path[PATH_MAX_LENGTH];
- const struct retro_subsystem_memory_info *mem =
- (const struct retro_subsystem_memory_info*)
- &info->roms[i].memory[j];
- path[0] = ext[0] = '\0';
- strcpy_literal(ext, ".");
- strlcat(ext, mem->extension, sizeof(ext));
- strlcpy(savename,
- p_rarch->subsystem_fullpaths->elems[i].data,
- sizeof(savename));
- path_remove_extension(savename);
- if (path_is_directory(savefile_dir))
- {
- /* Use SRAM dir */
- /* Redirect content fullpath to save directory. */
- strlcpy(path, savefile_dir, sizeof(path));
- fill_pathname_dir(path, savename, ext, sizeof(path));
- }
- else
- fill_pathname(path, savename, ext, sizeof(path));
- RARCH_LOG("%s \"%s\".\n",
- msg_hash_to_str(MSG_REDIRECTING_SAVEFILE_TO),
- path);
- attr.i = mem->type;
- string_list_append((struct string_list*)savefile_ptr_get(),
- path, attr);
- }
- }
- }
- if (global)
- {
- /* Let other relevant paths be inferred from the main SRAM location. */
- if (!retroarch_override_setting_is_set(
- RARCH_OVERRIDE_SETTING_SAVE_PATH, NULL))
- fill_pathname_noext(global->name.savefile,
- p_rarch->path_main_basename,
- ".srm",
- sizeof(global->name.savefile));
- if (path_is_directory(global->name.savefile))
- {
- fill_pathname_dir(global->name.savefile,
- p_rarch->path_main_basename,
- ".srm",
- sizeof(global->name.savefile));
- RARCH_LOG("%s \"%s\".\n",
- msg_hash_to_str(MSG_REDIRECTING_SAVEFILE_TO),
- global->name.savefile);
- }
- }
- return true;
- }
- static void path_init_savefile(struct rarch_state *p_rarch)
- {
- bool should_sram_be_used = p_rarch->rarch_use_sram
- && !p_rarch->rarch_is_sram_save_disabled;
- p_rarch->rarch_use_sram = should_sram_be_used;
- if (!p_rarch->rarch_use_sram)
- {
- RARCH_LOG("[SRAM]: %s\n",
- msg_hash_to_str(MSG_SRAM_WILL_NOT_BE_SAVED));
- return;
- }
- command_event(CMD_EVENT_AUTOSAVE_INIT, NULL);
- }
- static void path_init_savefile_internal(
- global_t *global,
- struct rarch_state *p_rarch)
- {
- path_deinit_savefile();
- path_init_savefile_new();
- if (!path_init_subsystem(p_rarch))
- path_init_savefile_rtc(global->name.savefile);
- }
- static void path_fill_names(struct rarch_state *p_rarch)
- {
- global_t *global = &p_rarch->g_extern;
- path_init_savefile_internal(global, p_rarch);
- #ifdef HAVE_BSV_MOVIE
- if (global)
- strlcpy(p_rarch->bsv_movie_state.movie_path,
- global->name.savefile,
- sizeof(p_rarch->bsv_movie_state.movie_path));
- #endif
- if (string_is_empty(p_rarch->path_main_basename))
- return;
- if (global)
- {
- if (string_is_empty(global->name.ups))
- fill_pathname_noext(global->name.ups,
- p_rarch->path_main_basename,
- ".ups",
- sizeof(global->name.ups));
- if (string_is_empty(global->name.bps))
- fill_pathname_noext(global->name.bps,
- p_rarch->path_main_basename,
- ".bps",
- sizeof(global->name.bps));
- if (string_is_empty(global->name.ips))
- fill_pathname_noext(global->name.ips,
- p_rarch->path_main_basename,
- ".ips",
- sizeof(global->name.ips));
- }
- }
- char *path_get_ptr(enum rarch_path_type type)
- {
- struct rarch_state *p_rarch = &rarch_st;
- switch (type)
- {
- case RARCH_PATH_CONTENT:
- return p_rarch->path_content;
- case RARCH_PATH_DEFAULT_SHADER_PRESET:
- return p_rarch->path_default_shader_preset;
- case RARCH_PATH_BASENAME:
- return p_rarch->path_main_basename;
- case RARCH_PATH_CORE_OPTIONS:
- if (!path_is_empty(RARCH_PATH_CORE_OPTIONS))
- return p_rarch->path_core_options_file;
- break;
- case RARCH_PATH_SUBSYSTEM:
- return p_rarch->subsystem_path;
- case RARCH_PATH_CONFIG:
- if (!path_is_empty(RARCH_PATH_CONFIG))
- return p_rarch->path_config_file;
- break;
- case RARCH_PATH_CONFIG_APPEND:
- if (!path_is_empty(RARCH_PATH_CONFIG_APPEND))
- return p_rarch->path_config_append_file;
- break;
- case RARCH_PATH_CORE:
- return p_rarch->path_libretro;
- case RARCH_PATH_NONE:
- case RARCH_PATH_NAMES:
- break;
- }
- return NULL;
- }
- const char *path_get(enum rarch_path_type type)
- {
- struct rarch_state *p_rarch = &rarch_st;
- switch (type)
- {
- case RARCH_PATH_CONTENT:
- return p_rarch->path_content;
- case RARCH_PATH_DEFAULT_SHADER_PRESET:
- return p_rarch->path_default_shader_preset;
- case RARCH_PATH_BASENAME:
- return p_rarch->path_main_basename;
- case RARCH_PATH_CORE_OPTIONS:
- if (!path_is_empty(RARCH_PATH_CORE_OPTIONS))
- return p_rarch->path_core_options_file;
- break;
- case RARCH_PATH_SUBSYSTEM:
- return p_rarch->subsystem_path;
- case RARCH_PATH_CONFIG:
- if (!path_is_empty(RARCH_PATH_CONFIG))
- return p_rarch->path_config_file;
- break;
- case RARCH_PATH_CONFIG_APPEND:
- if (!path_is_empty(RARCH_PATH_CONFIG_APPEND))
- return p_rarch->path_config_append_file;
- break;
- case RARCH_PATH_CORE:
- return p_rarch->path_libretro;
- case RARCH_PATH_NONE:
- case RARCH_PATH_NAMES:
- break;
- }
- return NULL;
- }
- size_t path_get_realsize(enum rarch_path_type type)
- {
- struct rarch_state *p_rarch = &rarch_st;
- switch (type)
- {
- case RARCH_PATH_CONTENT:
- return sizeof(p_rarch->path_content);
- case RARCH_PATH_DEFAULT_SHADER_PRESET:
- return sizeof(p_rarch->path_default_shader_preset);
- case RARCH_PATH_BASENAME:
- return sizeof(p_rarch->path_main_basename);
- case RARCH_PATH_CORE_OPTIONS:
- return sizeof(p_rarch->path_core_options_file);
- case RARCH_PATH_SUBSYSTEM:
- return sizeof(p_rarch->subsystem_path);
- case RARCH_PATH_CONFIG:
- return sizeof(p_rarch->path_config_file);
- case RARCH_PATH_CONFIG_APPEND:
- return sizeof(p_rarch->path_config_append_file);
- case RARCH_PATH_CORE:
- return sizeof(p_rarch->path_libretro);
- case RARCH_PATH_NONE:
- case RARCH_PATH_NAMES:
- break;
- }
- return 0;
- }
- static void path_set_names(struct rarch_state *p_rarch, const char *path)
- {
- global_t *global = &p_rarch->g_extern;
- path_set_basename(p_rarch, path);
- if (global)
- {
- if (!retroarch_override_setting_is_set(
- RARCH_OVERRIDE_SETTING_SAVE_PATH, NULL))
- fill_pathname_noext(global->name.savefile,
- p_rarch->path_main_basename,
- ".srm", sizeof(global->name.savefile));
- if (!retroarch_override_setting_is_set(
- RARCH_OVERRIDE_SETTING_STATE_PATH, NULL))
- fill_pathname_noext(global->name.savestate,
- p_rarch->path_main_basename,
- ".state", sizeof(global->name.savestate));
- #ifdef HAVE_CHEATS
- if (!string_is_empty(p_rarch->path_main_basename))
- fill_pathname_noext(global->name.cheatfile,
- p_rarch->path_main_basename,
- ".cht", sizeof(global->name.cheatfile));
- #endif
- }
- path_set_redirect(p_rarch);
- }
- bool path_set(enum rarch_path_type type, const char *path)
- {
- struct rarch_state *p_rarch = &rarch_st;
- if (!path)
- return false;
- switch (type)
- {
- case RARCH_PATH_BASENAME:
- strlcpy(p_rarch->path_main_basename, path,
- sizeof(p_rarch->path_main_basename));
- break;
- case RARCH_PATH_NAMES:
- path_set_names(p_rarch, path);
- break;
- case RARCH_PATH_CORE:
- strlcpy(p_rarch->path_libretro, path,
- sizeof(p_rarch->path_libretro));
- break;
- case RARCH_PATH_DEFAULT_SHADER_PRESET:
- strlcpy(p_rarch->path_default_shader_preset, path,
- sizeof(p_rarch->path_default_shader_preset));
- break;
- case RARCH_PATH_CONFIG_APPEND:
- strlcpy(p_rarch->path_config_append_file, path,
- sizeof(p_rarch->path_config_append_file));
- break;
- case RARCH_PATH_CONFIG:
- strlcpy(p_rarch->path_config_file, path,
- sizeof(p_rarch->path_config_file));
- break;
- case RARCH_PATH_SUBSYSTEM:
- strlcpy(p_rarch->subsystem_path, path,
- sizeof(p_rarch->subsystem_path));
- break;
- case RARCH_PATH_CORE_OPTIONS:
- strlcpy(p_rarch->path_core_options_file, path,
- sizeof(p_rarch->path_core_options_file));
- break;
- case RARCH_PATH_CONTENT:
- strlcpy(p_rarch->path_content, path,
- sizeof(p_rarch->path_content));
- break;
- case RARCH_PATH_NONE:
- break;
- }
- return true;
- }
- bool path_is_empty(enum rarch_path_type type)
- {
- struct rarch_state *p_rarch = &rarch_st;
- switch (type)
- {
- case RARCH_PATH_DEFAULT_SHADER_PRESET:
- if (string_is_empty(p_rarch->path_default_shader_preset))
- return true;
- break;
- case RARCH_PATH_SUBSYSTEM:
- if (string_is_empty(p_rarch->subsystem_path))
- return true;
- break;
- case RARCH_PATH_CONFIG:
- if (string_is_empty(p_rarch->path_config_file))
- return true;
- break;
- case RARCH_PATH_CORE_OPTIONS:
- if (string_is_empty(p_rarch->path_core_options_file))
- return true;
- break;
- case RARCH_PATH_CONFIG_APPEND:
- if (string_is_empty(p_rarch->path_config_append_file))
- return true;
- break;
- case RARCH_PATH_CONTENT:
- if (string_is_empty(p_rarch->path_content))
- return true;
- break;
- case RARCH_PATH_CORE:
- if (string_is_empty(p_rarch->path_libretro))
- return true;
- break;
- case RARCH_PATH_BASENAME:
- if (string_is_empty(p_rarch->path_main_basename))
- return true;
- break;
- case RARCH_PATH_NONE:
- case RARCH_PATH_NAMES:
- break;
- }
- return false;
- }
- void path_clear(enum rarch_path_type type)
- {
- struct rarch_state *p_rarch = &rarch_st;
- switch (type)
- {
- case RARCH_PATH_SUBSYSTEM:
- *p_rarch->subsystem_path = '\0';
- break;
- case RARCH_PATH_CORE:
- *p_rarch->path_libretro = '\0';
- break;
- case RARCH_PATH_CONFIG:
- *p_rarch->path_config_file = '\0';
- break;
- case RARCH_PATH_CONTENT:
- *p_rarch->path_content = '\0';
- break;
- case RARCH_PATH_BASENAME:
- *p_rarch->path_main_basename = '\0';
- break;
- case RARCH_PATH_CORE_OPTIONS:
- *p_rarch->path_core_options_file = '\0';
- break;
- case RARCH_PATH_DEFAULT_SHADER_PRESET:
- *p_rarch->path_default_shader_preset = '\0';
- break;
- case RARCH_PATH_CONFIG_APPEND:
- *p_rarch->path_config_append_file = '\0';
- break;
- case RARCH_PATH_NONE:
- case RARCH_PATH_NAMES:
- break;
- }
- }
- static void path_clear_all(void)
- {
- path_clear(RARCH_PATH_CONTENT);
- path_clear(RARCH_PATH_CONFIG);
- path_clear(RARCH_PATH_CONFIG_APPEND);
- path_clear(RARCH_PATH_CORE_OPTIONS);
- path_clear(RARCH_PATH_BASENAME);
- }
- enum rarch_content_type path_is_media_type(const char *path)
- {
- char ext_lower[128];
- ext_lower[0] = '\0';
- strlcpy(ext_lower, path_get_extension(path), sizeof(ext_lower));
- string_to_lower(ext_lower);
- /* hack, to detect livestreams so the ffmpeg core can be started */
- if (string_starts_with_size(path, "udp://", STRLEN_CONST("udp://")) ||
- string_starts_with_size(path, "http://", STRLEN_CONST("http://")) ||
- string_starts_with_size(path, "https://", STRLEN_CONST("https://")) ||
- string_starts_with_size(path, "tcp://", STRLEN_CONST("tcp://")) ||
- string_starts_with_size(path, "rtmp://", STRLEN_CONST("rtmp://")) ||
- string_starts_with_size(path, "rtp://", STRLEN_CONST("rtp://")))
- return RARCH_CONTENT_MOVIE;
- switch (msg_hash_to_file_type(msg_hash_calculate(ext_lower)))
- {
- #if defined(HAVE_FFMPEG) || defined(HAVE_MPV)
- case FILE_TYPE_OGM:
- case FILE_TYPE_MKV:
- case FILE_TYPE_AVI:
- case FILE_TYPE_MP4:
- case FILE_TYPE_FLV:
- case FILE_TYPE_WEBM:
- case FILE_TYPE_3GP:
- case FILE_TYPE_3G2:
- case FILE_TYPE_F4F:
- case FILE_TYPE_F4V:
- case FILE_TYPE_MOV:
- case FILE_TYPE_WMV:
- case FILE_TYPE_MPG:
- case FILE_TYPE_MPEG:
- case FILE_TYPE_VOB:
- case FILE_TYPE_ASF:
- case FILE_TYPE_DIVX:
- case FILE_TYPE_M2P:
- case FILE_TYPE_M2TS:
- case FILE_TYPE_PS:
- case FILE_TYPE_TS:
- case FILE_TYPE_MXF:
- return RARCH_CONTENT_MOVIE;
- case FILE_TYPE_WMA:
- case FILE_TYPE_OGG:
- case FILE_TYPE_MP3:
- case FILE_TYPE_M4A:
- case FILE_TYPE_FLAC:
- case FILE_TYPE_WAV:
- return RARCH_CONTENT_MUSIC;
- #endif
- #ifdef HAVE_IMAGEVIEWER
- case FILE_TYPE_JPEG:
- case FILE_TYPE_PNG:
- case FILE_TYPE_TGA:
- case FILE_TYPE_BMP:
- return RARCH_CONTENT_IMAGE;
- #endif
- #ifdef HAVE_IBXM
- case FILE_TYPE_MOD:
- case FILE_TYPE_S3M:
- case FILE_TYPE_XM:
- return RARCH_CONTENT_MUSIC;
- #endif
- #ifdef HAVE_GONG
- case FILE_TYPE_GONG:
- return RARCH_CONTENT_GONG;
- #endif
- case FILE_TYPE_NONE:
- default:
- break;
- }
- return RARCH_CONTENT_NONE;
- }
- static void path_deinit_subsystem(struct rarch_state *p_rarch)
- {
- if (p_rarch->subsystem_fullpaths)
- string_list_free(p_rarch->subsystem_fullpaths);
- p_rarch->subsystem_fullpaths = NULL;
- }
- static void dir_free_shader(struct rarch_state *p_rarch)
- {
- struct rarch_dir_shader_list *dir_list =
- (struct rarch_dir_shader_list*)&p_rarch->dir_shader_list;
- settings_t *settings = p_rarch->configuration_settings;
- bool shader_remember_last_dir = settings->bools.video_shader_remember_last_dir;
- if (dir_list->shader_list)
- {
- dir_list_free(dir_list->shader_list);
- dir_list->shader_list = NULL;
- }
- if (dir_list->directory)
- {
- free(dir_list->directory);
- dir_list->directory = NULL;
- }
- dir_list->selection = 0;
- dir_list->shader_loaded = false;
- dir_list->remember_last_preset_dir = shader_remember_last_dir;
- }
- #if defined(HAVE_CG) || defined(HAVE_GLSL) || defined(HAVE_SLANG) || defined(HAVE_HLSL)
- static bool dir_init_shader_internal(
- struct rarch_state *p_rarch,
- const char *shader_dir,
- const char *shader_file_name,
- bool show_hidden_files)
- {
- struct rarch_dir_shader_list *dir_list = (struct rarch_dir_shader_list*)
- &p_rarch->dir_shader_list;
- struct string_list *new_list = dir_list_new_special(
- shader_dir, DIR_LIST_SHADERS, NULL, show_hidden_files);
- settings_t *settings = p_rarch->configuration_settings;
- bool shader_remember_last_dir = settings->bools.video_shader_remember_last_dir;
- bool search_file_name = shader_remember_last_dir &&
- !string_is_empty(shader_file_name);
- bool file_name_found = false;
- size_t i;
- if (!new_list)
- return false;
- if (new_list->size < 1)
- {
- dir_list_free(new_list);
- return false;
- }
- dir_list_sort(new_list, false);
- dir_list->shader_list = new_list;
- dir_list->directory = strdup(shader_dir);
- dir_list->selection = 0;
- dir_list->shader_loaded = false;
- dir_list->remember_last_preset_dir = shader_remember_last_dir;
- for (i = 0; i < new_list->size; i++)
- {
- const char *file_path = new_list->elems[i].data;
- if (string_is_empty(file_path))
- continue;
- RARCH_LOG("[Shaders]: %s \"%s\"\n",
- msg_hash_to_str(MSG_FOUND_SHADER),
- file_path);
- /* If a shader file name has been provided,
- * search the list for a match and set 'selection'
- * index if found */
- if (search_file_name && !file_name_found)
- {
- const char *file_name = path_basename(file_path);
- if (!string_is_empty(file_name) &&
- string_is_equal(file_name, shader_file_name))
- {
- dir_list->selection = i;
- file_name_found = true;
- }
- }
- }
- return true;
- }
- static void dir_init_shader(struct rarch_state *p_rarch)
- {
- settings_t *settings = p_rarch->configuration_settings;
- bool show_hidden_files = settings->bools.show_hidden_files;
- bool shader_remember_last_dir = settings->bools.video_shader_remember_last_dir;
- const char *directory_video_shader = settings->paths.directory_video_shader;
- const char *directory_menu_config = settings->paths.directory_menu_config;
- const char *last_shader_preset_dir = NULL;
- const char *last_shader_preset_file_name = NULL;
- #if defined(HAVE_MENU)
- enum rarch_shader_type last_shader_preset_type = menu_driver_get_last_shader_preset_type();
- menu_driver_get_last_shader_preset_path(
- &last_shader_preset_dir, &last_shader_preset_file_name);
- #else
- enum rarch_shader_type last_shader_preset_type = RARCH_SHADER_NONE;
- #endif
- /* Always free existing shader list */
- dir_free_shader(p_rarch);
- /* Try directory of last selected shader preset */
- if (shader_remember_last_dir &&
- (last_shader_preset_type != RARCH_SHADER_NONE) &&
- !string_is_empty(last_shader_preset_dir) &&
- dir_init_shader_internal(p_rarch,
- last_shader_preset_dir,
- last_shader_preset_file_name,
- show_hidden_files))
- return;
- /* Try video shaders directory */
- if (!string_is_empty(directory_video_shader) &&
- dir_init_shader_internal(
- p_rarch, directory_video_shader, NULL, show_hidden_files))
- return;
- /* Try config directory */
- if (!string_is_empty(directory_menu_config) &&
- dir_init_shader_internal(
- p_rarch, directory_menu_config, NULL, show_hidden_files))
- return;
- /* Try 'top level' directory containing main
- * RetroArch config file */
- if (!path_is_empty(RARCH_PATH_CONFIG))
- {
- char *rarch_config_directory = strdup(path_get(RARCH_PATH_CONFIG));
- path_basedir(rarch_config_directory);
- if (!string_is_empty(rarch_config_directory))
- dir_init_shader_internal(
- p_rarch, rarch_config_directory, NULL, show_hidden_files);
- free(rarch_config_directory);
- }
- }
- /**
- * dir_check_shader:
- * @pressed_next : Was next shader key pressed?
- * @pressed_prev : Was previous shader key pressed?
- *
- * Checks if any one of the shader keys has been pressed for this frame:
- * a) Next shader index.
- * b) Previous shader index.
- *
- * Will also immediately apply the shader.
- **/
- static void dir_check_shader(struct rarch_state *p_rarch,
- bool pressed_next, bool pressed_prev)
- {
- struct rarch_dir_shader_list *dir_list = (struct rarch_dir_shader_list*)
- &p_rarch->dir_shader_list;
- settings_t *settings = p_rarch->configuration_settings;
- bool shader_remember_last_dir = settings->bools.video_shader_remember_last_dir;
- const char *last_shader_preset_dir = NULL;
- const char *last_shader_preset_file_name = NULL;
- const char *set_shader_path = NULL;
- bool dir_list_initialised = false;
- #if defined(HAVE_MENU)
- enum rarch_shader_type last_shader_preset_type = menu_driver_get_last_shader_preset_type();
- menu_driver_get_last_shader_preset_path(
- &last_shader_preset_dir, &last_shader_preset_file_name);
- #else
- enum rarch_shader_type last_shader_preset_type = RARCH_SHADER_NONE;
- #endif
- /* Check whether shader list needs to be
- * (re)initialised */
- if (!dir_list->shader_list ||
- (dir_list->remember_last_preset_dir != shader_remember_last_dir) ||
- (shader_remember_last_dir &&
- (last_shader_preset_type != RARCH_SHADER_NONE) &&
- !string_is_equal(dir_list->directory, last_shader_preset_dir)))
- {
- dir_init_shader(p_rarch);
- dir_list_initialised = true;
- }
- if (!dir_list->shader_list ||
- (dir_list->shader_list->size < 1))
- return;
- /* Check whether a 'last used' shader file
- * name is provided
- * > Note: We can end up calling
- * string_is_equal(dir_list->directory, last_shader_preset_dir)
- * twice. This is wasteful, but we cannot safely cache
- * the first result since dir_init_shader() is called
- * in-between the two invocations... */
- if (shader_remember_last_dir &&
- (last_shader_preset_type != RARCH_SHADER_NONE) &&
- string_is_equal(dir_list->directory, last_shader_preset_dir) &&
- !string_is_empty(last_shader_preset_file_name))
- {
- /* Ensure that we start with a dir_list selection
- * index matching the last used shader */
- if (!dir_list_initialised)
- {
- const char *current_file_path = NULL;
- const char *current_file_name = NULL;
- if (dir_list->selection < dir_list->shader_list->size)
- current_file_path = dir_list->shader_list->elems[dir_list->selection].data;
- if (!string_is_empty(current_file_path))
- current_file_name = path_basename(current_file_path);
- if (!string_is_empty(current_file_name) &&
- !string_is_equal(current_file_name, last_shader_preset_file_name))
- {
- size_t i;
- for (i = 0; i < dir_list->shader_list->size; i++)
- {
- const char *file_path = dir_list->shader_list->elems[i].data;
- const char *file_name = NULL;
- if (string_is_empty(file_path))
- continue;
- file_name = path_basename(file_path);
- if (string_is_empty(file_name))
- continue;
- if (string_is_equal(file_name, last_shader_preset_file_name))
- {
- dir_list->selection = i;
- break;
- }
- }
- }
- }
- /* Check whether the shader referenced by the
- * current selection index is already loaded */
- if (!dir_list->shader_loaded)
- {
- struct video_shader *shader = menu_shader_get();
- if (shader && !string_is_empty(shader->loaded_preset_path))
- {
- char last_shader_path[PATH_MAX_LENGTH];
- last_shader_path[0] = '\0';
- fill_pathname_join(last_shader_path,
- last_shader_preset_dir, last_shader_preset_file_name,
- sizeof(last_shader_path));
- if (string_is_equal(last_shader_path, shader->loaded_preset_path))
- dir_list->shader_loaded = true;
- }
- }
- }
- /* Select next shader in list */
- if (pressed_next)
- {
- /* Only increment selection if a shader
- * from this list has already been loaded
- * (otherwise first entry in the list may
- * be skipped) */
- if (dir_list->shader_loaded)
- {
- if (dir_list->selection < dir_list->shader_list->size - 1)
- dir_list->selection++;
- else
- dir_list->selection = 0;
- }
- }
- /* Select previous shader in list */
- else if (pressed_prev)
- {
- if (dir_list->selection > 0)
- dir_list->selection--;
- else
- dir_list->selection = dir_list->shader_list->size - 1;
- }
- else
- return;
- set_shader_path = dir_list->shader_list->elems[dir_list->selection].data;
- #if defined(HAVE_MENU)
- menu_driver_set_last_shader_preset_path(set_shader_path);
- #endif
- command_set_shader(set_shader_path);
- dir_list->shader_loaded = true;
- }
- #endif
- /* get size functions */
- size_t dir_get_size(enum rarch_dir_type type)
- {
- struct rarch_state *p_rarch = &rarch_st;
- switch (type)
- {
- case RARCH_DIR_SYSTEM:
- return sizeof(p_rarch->dir_system);
- case RARCH_DIR_SAVESTATE:
- return sizeof(p_rarch->dir_savestate);
- case RARCH_DIR_CURRENT_SAVESTATE:
- return sizeof(p_rarch->current_savestate_dir);
- case RARCH_DIR_SAVEFILE:
- return sizeof(p_rarch->dir_savefile);
- case RARCH_DIR_CURRENT_SAVEFILE:
- return sizeof(p_rarch->current_savefile_dir);
- case RARCH_DIR_NONE:
- break;
- }
- return 0;
- }
- /* clear functions */
- void dir_clear(enum rarch_dir_type type)
- {
- struct rarch_state *p_rarch = &rarch_st;
- switch (type)
- {
- case RARCH_DIR_SAVEFILE:
- *p_rarch->dir_savefile = '\0';
- break;
- case RARCH_DIR_CURRENT_SAVEFILE:
- *p_rarch->current_savefile_dir = '\0';
- break;
- case RARCH_DIR_SAVESTATE:
- *p_rarch->dir_savestate = '\0';
- break;
- case RARCH_DIR_CURRENT_SAVESTATE:
- *p_rarch->current_savestate_dir = '\0';
- break;
- case RARCH_DIR_SYSTEM:
- *p_rarch->dir_system = '\0';
- break;
- case RARCH_DIR_NONE:
- break;
- }
- }
- static void dir_clear_all(void)
- {
- dir_clear(RARCH_DIR_SYSTEM);
- dir_clear(RARCH_DIR_SAVEFILE);
- dir_clear(RARCH_DIR_SAVESTATE);
- }
- /* get ptr functions */
- char *dir_get_ptr(enum rarch_dir_type type)
- {
- struct rarch_state *p_rarch = &rarch_st;
- switch (type)
- {
- case RARCH_DIR_SAVEFILE:
- return p_rarch->dir_savefile;
- case RARCH_DIR_CURRENT_SAVEFILE:
- return p_rarch->current_savefile_dir;
- case RARCH_DIR_SAVESTATE:
- return p_rarch->dir_savestate;
- case RARCH_DIR_CURRENT_SAVESTATE:
- return p_rarch->current_savestate_dir;
- case RARCH_DIR_SYSTEM:
- return p_rarch->dir_system;
- case RARCH_DIR_NONE:
- break;
- }
- return NULL;
- }
- void dir_set(enum rarch_dir_type type, const char *path)
- {
- struct rarch_state *p_rarch = &rarch_st;
- switch (type)
- {
- case RARCH_DIR_CURRENT_SAVEFILE:
- strlcpy(p_rarch->current_savefile_dir, path,
- sizeof(p_rarch->current_savefile_dir));
- break;
- case RARCH_DIR_SAVEFILE:
- strlcpy(p_rarch->dir_savefile, path,
- sizeof(p_rarch->dir_savefile));
- break;
- case RARCH_DIR_CURRENT_SAVESTATE:
- strlcpy(p_rarch->current_savestate_dir, path,
- sizeof(p_rarch->current_savestate_dir));
- break;
- case RARCH_DIR_SAVESTATE:
- strlcpy(p_rarch->dir_savestate, path,
- sizeof(p_rarch->dir_savestate));
- break;
- case RARCH_DIR_SYSTEM:
- strlcpy(p_rarch->dir_system, path,
- sizeof(p_rarch->dir_system));
- break;
- case RARCH_DIR_NONE:
- break;
- }
- }
- void dir_check_defaults(const char *custom_ini_path)
- {
- size_t i;
- /* Early return for people with a custom folder setup
- * so it doesn't create unnecessary directories */
- if (!string_is_empty(custom_ini_path) &&
- path_is_valid(custom_ini_path))
- return;
- for (i = 0; i < DEFAULT_DIR_LAST; i++)
- {
- const char *dir_path = g_defaults.dirs[i];
- char new_path[PATH_MAX_LENGTH];
- if (string_is_empty(dir_path))
- continue;
- new_path[0] = '\0';
- fill_pathname_expand_special(new_path,
- dir_path, sizeof(new_path));
- if (!path_is_directory(new_path))
- path_mkdir(new_path);
- }
- }
- #ifdef HAVE_ACCESSIBILITY
- static bool is_accessibility_enabled(struct rarch_state *p_rarch)
- {
- settings_t *settings = p_rarch->configuration_settings;
- bool accessibility_enable = settings->bools.accessibility_enable;
- bool accessibility_enabled = p_rarch->accessibility_enabled;
- if (accessibility_enabled || accessibility_enable)
- return true;
- return false;
- }
- #endif
- bool gfx_widgets_ready(void)
- {
- #ifdef HAVE_GFX_WIDGETS
- struct rarch_state *p_rarch = &rarch_st;
- return p_rarch->widgets_active;
- #else
- return false;
- #endif
- }
- #ifdef HAVE_MENU
- static void menu_input_search_cb(void *userdata, const char *str)
- {
- const char *label = NULL;
- unsigned type = MENU_SETTINGS_NONE;
- struct rarch_state *p_rarch = &rarch_st;
- struct menu_state *menu_st = &p_rarch->menu_driver_state;
- if (string_is_empty(str))
- goto end;
- /* Determine whether we are currently
- * viewing a menu list with 'search
- * filter' support */
- file_list_get_last(MENU_LIST_GET(menu_st->entries.list, 0),
- NULL, &label, &type, NULL);
- if (menu_driver_search_filter_enabled(label, type))
- {
- /* Add search term */
- if (menu_driver_search_push(str))
- {
- bool refresh = false;
- /* Reset navigation pointer */
- menu_st->selection_ptr = 0;
- menu_driver_navigation_set(false);
- /* Refresh menu */
- menu_entries_ctl(MENU_ENTRIES_CTL_SET_REFRESH, &refresh);
- menu_driver_ctl(RARCH_MENU_CTL_SET_PREVENT_POPULATE, NULL);
- }
- }
- /* Perform a regular search: jump to the
- * first matching entry */
- else
- {
- size_t idx = 0;
- struct rarch_state *p_rarch = &rarch_st;
- struct menu_state *menu_st = &p_rarch->menu_driver_state;
- menu_list_t *menu_list = menu_st->entries.list;
- file_list_t *selection_buf = menu_list ? MENU_LIST_GET_SELECTION(menu_list, (unsigned)0) : NULL;
- if (!selection_buf)
- goto end;
- if (file_list_search(selection_buf, str, &idx))
- {
- menu_st->selection_ptr = idx;
- menu_driver_navigation_set(true);
- }
- }
- end:
- menu_input_dialog_end();
- }
- const char *menu_input_dialog_get_label_buffer(void)
- {
- struct rarch_state *p_rarch = &rarch_st;
- return p_rarch->menu_input_dialog_keyboard_label;
- }
- const char *menu_input_dialog_get_label_setting_buffer(void)
- {
- struct rarch_state *p_rarch = &rarch_st;
- return p_rarch->menu_input_dialog_keyboard_label_setting;
- }
- void menu_input_dialog_end(void)
- {
- struct rarch_state *p_rarch = &rarch_st;
- p_rarch->menu_input_dialog_keyboard_type = 0;
- p_rarch->menu_input_dialog_keyboard_idx = 0;
- p_rarch->menu_input_dialog_keyboard_display = false;
- p_rarch->menu_input_dialog_keyboard_label[0] = '\0';
- p_rarch->menu_input_dialog_keyboard_label_setting[0] = '\0';
- /* Avoid triggering states on pressing return. */
- /* Inhibits input for 2 frames
- * > Required, since input is ignored for 1 frame
- * after certain events - e.g. closing the OSK */
- p_rarch->input_driver_flushing_input = 2;
- }
- const char *menu_input_dialog_get_buffer(void)
- {
- struct rarch_state *p_rarch = &rarch_st;
- if (!(*p_rarch->menu_input_dialog_keyboard_buffer))
- return "";
- return *p_rarch->menu_input_dialog_keyboard_buffer;
- }
- unsigned menu_input_dialog_get_kb_idx(void)
- {
- struct rarch_state *p_rarch = &rarch_st;
- return p_rarch->menu_input_dialog_keyboard_idx;
- }
- bool menu_input_dialog_start_search(void)
- {
- struct rarch_state *p_rarch = &rarch_st;
- menu_handle_t *menu = p_rarch->menu_driver_data;
- if (!menu)
- return false;
- p_rarch->menu_input_dialog_keyboard_display = true;
- strlcpy(p_rarch->menu_input_dialog_keyboard_label,
- msg_hash_to_str(MENU_ENUM_LABEL_VALUE_SEARCH),
- sizeof(p_rarch->menu_input_dialog_keyboard_label));
- if (p_rarch->keyboard_line.buffer)
- free(p_rarch->keyboard_line.buffer);
- p_rarch->keyboard_line.buffer = NULL;
- p_rarch->keyboard_line.ptr = 0;
- p_rarch->keyboard_line.size = 0;
- p_rarch->keyboard_line.cb = NULL;
- p_rarch->keyboard_line.userdata = NULL;
- p_rarch->keyboard_line.enabled = false;
- #ifdef HAVE_ACCESSIBILITY
- if (is_accessibility_enabled(p_rarch))
- accessibility_speak_priority(p_rarch, (char*)
- msg_hash_to_str(MENU_ENUM_LABEL_VALUE_SEARCH), 10);
- #endif
- p_rarch->menu_input_dialog_keyboard_buffer =
- input_keyboard_start_line(menu,
- &p_rarch->keyboard_line,
- menu_input_search_cb);
- /* While reading keyboard line input, we have to block all hotkeys. */
- p_rarch->keyboard_mapping_blocked= true;
- return true;
- }
- bool menu_input_dialog_start(menu_input_ctx_line_t *line)
- {
- struct rarch_state *p_rarch = &rarch_st;
- menu_handle_t *menu = p_rarch->menu_driver_data;
- if (!line || !menu)
- return false;
- p_rarch->menu_input_dialog_keyboard_display = true;
- /* Only copy over the menu label and setting if they exist. */
- if (line->label)
- strlcpy(p_rarch->menu_input_dialog_keyboard_label,
- line->label,
- sizeof(p_rarch->menu_input_dialog_keyboard_label));
- if (line->label_setting)
- strlcpy(p_rarch->menu_input_dialog_keyboard_label_setting,
- line->label_setting,
- sizeof(p_rarch->menu_input_dialog_keyboard_label_setting));
- p_rarch->menu_input_dialog_keyboard_type = line->type;
- p_rarch->menu_input_dialog_keyboard_idx = line->idx;
- if (p_rarch->keyboard_line.buffer)
- free(p_rarch->keyboard_line.buffer);
- p_rarch->keyboard_line.buffer = NULL;
- p_rarch->keyboard_line.ptr = 0;
- p_rarch->keyboard_line.size = 0;
- p_rarch->keyboard_line.cb = NULL;
- p_rarch->keyboard_line.userdata = NULL;
- p_rarch->keyboard_line.enabled = false;
- #ifdef HAVE_ACCESSIBILITY
- if (is_accessibility_enabled(p_rarch))
- accessibility_speak_priority(p_rarch, "Keyboard input:", 10);
- #endif
- p_rarch->menu_input_dialog_keyboard_buffer =
- input_keyboard_start_line(menu,
- &p_rarch->keyboard_line,
- line->cb);
- /* While reading keyboard line input, we have to block all hotkeys. */
- p_rarch->keyboard_mapping_blocked= true;
- return true;
- }
- static void osk_update_last_codepoint(
- unsigned *last_codepoint,
- unsigned *last_codepoint_len,
- const char *word)
- {
- const char *letter = word;
- const char *pos = letter;
- for (;;)
- {
- unsigned codepoint = utf8_walk(&letter);
- if (letter[0] == 0)
- {
- *last_codepoint = codepoint;
- *last_codepoint_len = (unsigned)(letter - pos);
- break;
- }
- pos = letter;
- }
- }
- bool menu_input_dialog_get_display_kb(void)
- {
- struct rarch_state *p_rarch = &rarch_st;
- #ifdef HAVE_LIBNX
- SwkbdConfig kbd;
- Result rc;
- /* Indicates that we are "typing" from the swkbd
- * result to RetroArch with repeated calls to input_keyboard_event
- * This prevents input_keyboard_event from calling back
- * menu_input_dialog_get_display_kb, looping indefinintely */
- static bool typing = false;
- if (typing)
- return false;
- /* swkbd only works on "real" titles */
- if ( __nx_applet_type != AppletType_Application
- && __nx_applet_type != AppletType_SystemApplication)
- return p_rarch->menu_input_dialog_keyboard_display;
- if (!p_rarch->menu_input_dialog_keyboard_display)
- return false;
- rc = swkbdCreate(&kbd, 0);
- if (R_SUCCEEDED(rc))
- {
- unsigned i;
- char buf[LIBNX_SWKBD_LIMIT] = {'\0'};
- swkbdConfigMakePresetDefault(&kbd);
- swkbdConfigSetGuideText(&kbd,
- p_rarch->menu_input_dialog_keyboard_label);
- rc = swkbdShow(&kbd, buf, sizeof(buf));
- swkbdClose(&kbd);
- /* RetroArch uses key-by-key input
- so we need to simulate it */
- typing = true;
- for (i = 0; i < LIBNX_SWKBD_LIMIT; i++)
- {
- /* In case a previous "Enter" press closed the keyboard */
- if (!p_rarch->menu_input_dialog_keyboard_display)
- break;
- if (buf[i] == '\n' || buf[i] == '\0')
- input_keyboard_event(true, '\n', '\n', 0, RETRO_DEVICE_KEYBOARD);
- else
- {
- const char *word = &buf[i];
- /* input_keyboard_line_append expects a null-terminated
- string, so just make one (yes, the touch keyboard is
- a list of "null-terminated characters") */
- char oldchar = buf[i+1];
- buf[i+1] = '\0';
- input_keyboard_line_append(&p_rarch->keyboard_line, word);
- if (word[0] == 0)
- {
- p_rarch->osk_last_codepoint = 0;
- p_rarch->osk_last_codepoint_len = 0;
- }
- else
- osk_update_last_codepoint(
- &p_rarch->osk_last_codepoint,
- &p_rarch->osk_last_codepoint_len,
- word);
- buf[i+1] = oldchar;
- }
- }
- /* fail-safe */
- if (p_rarch->menu_input_dialog_keyboard_display)
- input_keyboard_event(true, '\n', '\n', 0, RETRO_DEVICE_KEYBOARD);
- typing = false;
- libnx_apply_overclock();
- return false;
- }
- libnx_apply_overclock();
- #endif
- return p_rarch->menu_input_dialog_keyboard_display;
- }
- /* Checks if the menu is still running */
- bool menu_driver_is_alive(void)
- {
- struct rarch_state *p_rarch = &rarch_st;
- return p_rarch->menu_driver_alive;
- }
- #endif
- #if defined(HAVE_RUNAHEAD)
- #if defined(HAVE_DYNAMIC) || defined(HAVE_DYLIB)
- static char *strcpy_alloc(const char *src)
- {
- char *result = NULL;
- size_t len = strlen(src);
- if (len == 0)
- return NULL;
- result = (char*)malloc(len + 1);
- strcpy_literal(result, src);
- return result;
- }
- #endif
- #endif
- /* MESSAGE QUEUE */
- static void retroarch_msg_queue_deinit(struct rarch_state *p_rarch)
- {
- RUNLOOP_MSG_QUEUE_LOCK(p_rarch);
- msg_queue_deinitialize(&p_rarch->runloop_msg_queue);
- RUNLOOP_MSG_QUEUE_UNLOCK(p_rarch);
- #ifdef HAVE_THREADS
- slock_free(p_rarch->runloop_msg_queue_lock);
- p_rarch->runloop_msg_queue_lock = NULL;
- #endif
- p_rarch->runloop_msg_queue_size = 0;
- }
- static void retroarch_msg_queue_init(struct rarch_state *p_rarch)
- {
- retroarch_msg_queue_deinit(p_rarch);
- msg_queue_initialize(&p_rarch->runloop_msg_queue, 8);
- #ifdef HAVE_THREADS
- p_rarch->runloop_msg_queue_lock = slock_new();
- #endif
- }
- #ifdef HAVE_THREADS
- static void retroarch_autosave_deinit(struct rarch_state *p_rarch)
- {
- const bool rarch_use_sram = p_rarch->rarch_use_sram;
- if (rarch_use_sram)
- autosave_deinit();
- }
- #endif
- /* COMMAND */
- #if defined(HAVE_COMMAND)
- #if (defined(HAVE_STDIN_CMD) || defined(HAVE_NETWORK_CMD))
- static void command_reply(
- struct rarch_state *p_rarch,
- const char * data, size_t len)
- {
- const enum cmd_source_t lastcmd_source = p_rarch->lastcmd_source;
- switch (lastcmd_source)
- {
- case CMD_STDIN:
- #ifdef HAVE_STDIN_CMD
- fwrite(data, 1,len, stdout);
- #endif
- break;
- case CMD_NETWORK:
- #ifdef HAVE_NETWORK_CMD
- sendto(p_rarch->lastcmd_net_fd, data, len, 0,
- (struct sockaddr*)&p_rarch->lastcmd_net_source,
- p_rarch->lastcmd_net_source_len);
- #endif
- break;
- case CMD_NONE:
- default:
- break;
- }
- }
- #endif
- static bool command_version(const char* arg)
- {
- char reply[256] = {0};
- #if (defined(HAVE_STDIN_CMD) || defined(HAVE_NETWORK_CMD))
- struct rarch_state *p_rarch = &rarch_st;
- #endif
- snprintf(reply, sizeof(reply), "%s\n", PACKAGE_VERSION);
- #if (defined(HAVE_STDIN_CMD) || defined(HAVE_NETWORK_CMD))
- command_reply(p_rarch, reply, strlen(reply));
- #endif
- return true;
- }
- static bool command_get_status(const char* arg)
- {
- char reply[4096] = {0};
- bool contentless = false;
- bool is_inited = false;
- struct rarch_state *p_rarch = &rarch_st;
- content_get_status(&contentless, &is_inited);
- if (!is_inited)
- strcpy_literal(reply, "GET_STATUS CONTENTLESS");
- else
- {
- /* add some content info */
- const char *status = "PLAYING";
- const char *content_name = path_basename(path_get(RARCH_PATH_BASENAME)); /* filename only without ext */
- int content_crc32 = content_get_crc();
- const char* system_id = NULL;
- core_info_t *core_info = NULL;
- core_info_get_current_core(&core_info);
- if (p_rarch->runloop_paused)
- status = "PAUSED";
- if (core_info)
- system_id = core_info->system_id;
- if (!system_id)
- system_id = p_rarch->runloop_system.info.library_name;
- snprintf(reply, sizeof(reply), "GET_STATUS %s %s,%s,crc32=%x\n", status, system_id, content_name, content_crc32);
- }
- command_reply(p_rarch, reply, strlen(reply));
- return true;
- }
- static bool command_show_osd_msg(const char* arg)
- {
- runloop_msg_queue_push(arg, 1, 180, false, NULL,
- MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO);
- return true;
- }
- static bool command_get_config_param(const char* arg)
- {
- char reply[8192] = {0};
- struct rarch_state *p_rarch = &rarch_st;
- const char *value = "unsupported";
- settings_t *settings = p_rarch->configuration_settings;
- bool video_fullscreen = settings->bools.video_fullscreen;
- const char *dir_runtime_log = settings->paths.directory_runtime_log;
- const char *log_dir = settings->paths.log_dir;
- const char *directory_cache = settings->paths.directory_cache;
- const char *directory_system = settings->paths.directory_system;
- const char *path_username = settings->paths.username;
- if (string_is_equal(arg, "video_fullscreen"))
- {
- if (video_fullscreen)
- value = "true";
- else
- value = "false";
- }
- else if (string_is_equal(arg, "savefile_directory"))
- value = p_rarch->dir_savefile;
- else if (string_is_equal(arg, "savestate_directory"))
- value = p_rarch->dir_savestate;
- else if (string_is_equal(arg, "runtime_log_directory"))
- value = dir_runtime_log;
- else if (string_is_equal(arg, "log_dir"))
- value = log_dir;
- else if (string_is_equal(arg, "cache_directory"))
- value = directory_cache;
- else if (string_is_equal(arg, "system_directory"))
- value = directory_system;
- else if (string_is_equal(arg, "netplay_nickname"))
- value = path_username;
- /* TODO: query any string */
- snprintf(reply, sizeof(reply), "GET_CONFIG_PARAM %s %s\n", arg, value);
- command_reply(p_rarch, reply, strlen(reply));
- return true;
- }
- #if defined(HAVE_CHEEVOS)
- static bool command_read_ram(const char *arg)
- {
- unsigned i;
- char *reply = NULL;
- const uint8_t *data = NULL;
- char *reply_at = NULL;
- unsigned int nbytes = 0;
- unsigned int alloc_size = 0;
- unsigned int addr = -1;
- unsigned int len = 0;
- struct rarch_state *p_rarch = &rarch_st;
- if (sscanf(arg, "%x %u", &addr, &nbytes) != 2)
- return true;
- /* We allocate more than needed, saving 20 bytes is not really relevant */
- alloc_size = 40 + nbytes * 3;
- reply = (char*)malloc(alloc_size);
- reply[0] = '\0';
- reply_at = reply + snprintf(
- reply, alloc_size - 1, "READ_CORE_RAM" " %x", addr);
- if ((data = rcheevos_patch_address(addr)))
- {
- for (i = 0; i < nbytes; i++)
- snprintf(reply_at + 3 * i, 4, " %.2X", data[i]);
- reply_at[3 * nbytes] = '\n';
- len = reply_at + 3 * nbytes + 1 - reply;
- }
- else
- {
- strlcpy(reply_at, " -1\n", sizeof(reply) - strlen(reply));
- len = reply_at + STRLEN_CONST(" -1\n") - reply;
- }
- command_reply(p_rarch, reply, len);
- free(reply);
- return true;
- }
- static bool command_write_ram(const char *arg)
- {
- unsigned int addr = (unsigned int)strtoul(arg, (char**)&arg, 16);
- uint8_t *data = (uint8_t *)rcheevos_patch_address(addr);
- if (!data)
- return false;
- if (rcheevos_hardcore_active())
- {
- RARCH_LOG("Achievements hardcore mode disabled by WRITE_CORE_RAM\n");
- rcheevos_pause_hardcore();
- }
- while (*arg)
- {
- *data = strtoul(arg, (char**)&arg, 16);
- data++;
- }
- return true;
- }
- #endif
- static const rarch_memory_descriptor_t* command_memory_get_descriptor(const rarch_memory_map_t* mmap, unsigned address)
- {
- const rarch_memory_descriptor_t* desc = mmap->descriptors;
- const rarch_memory_descriptor_t* end = desc + mmap->num_descriptors;
- for (; desc < end; desc++)
- {
- if (desc->core.select == 0)
- {
- /* if select is 0, attempt to explicitly match the address */
- if (address >= desc->core.start && address < desc->core.start + desc->core.len)
- return desc;
- }
- else
- {
- /* otherwise, attempt to match the address by matching the select bits */
- if (((desc->core.start ^ address) & desc->core.select) == 0)
- {
- /* sanity check - make sure the descriptor is large enough to hold the target address */
- if (address - desc->core.start < desc->core.len)
- return desc;
- }
- }
- }
- return NULL;
- }
- static uint8_t* command_memory_get_pointer(unsigned address, unsigned int* max_bytes, int for_write, char* reply_at, size_t len)
- {
- const rarch_system_info_t* system = runloop_get_system_info();
- if (!system || system->mmaps.num_descriptors == 0)
- strlcpy(reply_at, " -1 no memory map defined\n", len);
- else
- {
- const rarch_memory_descriptor_t* desc = command_memory_get_descriptor(&system->mmaps, address);
- if (!desc)
- strlcpy(reply_at, " -1 no descriptor for address\n", len);
- else if (!desc->core.ptr)
- strlcpy(reply_at, " -1 no data for descriptor\n", len);
- else if (for_write && (desc->core.flags & RETRO_MEMDESC_CONST))
- strlcpy(reply_at, " -1 descriptor data is readonly\n", len);
- else
- {
- const size_t offset = address - desc->core.start;
- *max_bytes = (desc->core.len - offset);
- return (uint8_t*)desc->core.ptr + desc->core.offset + offset;
- }
- }
- *max_bytes = 0;
- return NULL;
- }
- static bool command_read_memory(const char *arg)
- {
- unsigned i;
- char* reply = NULL;
- char* reply_at = NULL;
- const uint8_t* data = NULL;
- unsigned int nbytes = 0;
- unsigned int alloc_size = 0;
- unsigned int address = -1;
- unsigned int len = 0;
- struct rarch_state *p_rarch = &rarch_st;
- unsigned int max_bytes = 0;
- if (sscanf(arg, "%x %u", &address, &nbytes) != 2)
- return false;
- /* Ensure large enough to return all requested bytes or an error message */
- alloc_size = 64 + nbytes * 3;
- reply = (char*)malloc(alloc_size);
- reply_at = reply + snprintf(reply, alloc_size - 1, "READ_CORE_MEMORY %x", address);
- data = command_memory_get_pointer(address, &max_bytes, 0, reply_at, alloc_size - strlen(reply));
- if (data)
- {
- if (nbytes > max_bytes)
- nbytes = max_bytes;
- for (i = 0; i < nbytes; i++)
- snprintf(reply_at + 3 * i, 4, " %02X", data[i]);
- reply_at[3 * nbytes] = '\n';
- len = reply_at + 3 * nbytes + 1 - reply;
- }
- else
- len = strlen(reply);
- command_reply(p_rarch, reply, len);
- free(reply);
- return true;
- }
- static bool command_write_memory(const char *arg)
- {
- unsigned int address = (unsigned int)strtoul(arg, (char**)&arg, 16);
- unsigned int max_bytes = 0;
- struct rarch_state *p_rarch = &rarch_st;
- char reply[128] = "";
- char *reply_at = reply + snprintf(reply, sizeof(reply) - 1, "WRITE_CORE_MEMORY %x", address);
- uint8_t *data = command_memory_get_pointer(address, &max_bytes, 1, reply_at, sizeof(reply) - strlen(reply) - 1);
- if (data)
- {
- uint8_t* start = data;
- while (*arg && max_bytes > 0)
- {
- --max_bytes;
- *data = strtoul(arg, (char**)&arg, 16);
- data++;
- }
- snprintf(reply_at, sizeof(reply) - strlen(reply) - 1,
- " %u\n", (unsigned)(data - start));
- #ifdef HAVE_CHEEVOS
- if (rcheevos_hardcore_active())
- {
- RARCH_LOG("Achievements hardcore mode disabled by WRITE_CORE_MEMORY\n");
- rcheevos_pause_hardcore();
- }
- #endif
- }
- command_reply(p_rarch, reply, strlen(reply));
- return true;
- }
- #ifdef HAVE_NETWORK_CMD
- static bool command_get_arg(const char *tok,
- const char **arg, unsigned *index)
- {
- unsigned i;
- for (i = 0; i < ARRAY_SIZE(map); i++)
- {
- if (string_is_equal(tok, map[i].str))
- {
- if (arg)
- *arg = NULL;
- if (index)
- *index = i;
- return true;
- }
- }
- for (i = 0; i < ARRAY_SIZE(action_map); i++)
- {
- const char *str = strstr(tok, action_map[i].str);
- if (str == tok)
- {
- const char *argument = str + strlen(action_map[i].str);
- if (*argument != ' ' && *argument != '\0')
- return false;
- if (arg)
- *arg = argument + 1;
- if (index)
- *index = i;
- return true;
- }
- }
- return false;
- }
- static bool command_network_init(command_t *handle, uint16_t port)
- {
- struct addrinfo *res = NULL;
- int fd = socket_init((void**)&res, port,
- NULL, SOCKET_TYPE_DATAGRAM);
- RARCH_LOG("%s %hu.\n",
- msg_hash_to_str(MSG_BRINGING_UP_COMMAND_INTERFACE_ON_PORT),
- (unsigned short)port);
- if (fd < 0)
- goto error;
- handle->net_fd = fd;
- if (!socket_nonblock(handle->net_fd))
- goto error;
- if (!socket_bind(handle->net_fd, (void*)res))
- {
- RARCH_ERR("%s.\n",
- msg_hash_to_str(MSG_FAILED_TO_BIND_SOCKET));
- goto error;
- }
- freeaddrinfo_retro(res);
- return true;
- error:
- if (res)
- freeaddrinfo_retro(res);
- return false;
- }
- static bool command_verify(const char *cmd)
- {
- unsigned i;
- if (command_get_arg(cmd, NULL, NULL))
- return true;
- RARCH_ERR("Command \"%s\" is not recognized by the program.\n", cmd);
- RARCH_ERR("\tValid commands:\n");
- for (i = 0; i < sizeof(map) / sizeof(map[0]); i++)
- RARCH_ERR("\t\t%s\n", map[i].str);
- for (i = 0; i < sizeof(action_map) / sizeof(action_map[0]); i++)
- RARCH_ERR("\t\t%s %s\n", action_map[i].str, action_map[i].arg_desc);
- return false;
- }
- static bool command_network_send(const char *cmd_)
- {
- char *command = NULL;
- char *save = NULL;
- const char *cmd = NULL;
- if (!network_init())
- return false;
- if (!(command = strdup(cmd_)))
- return false;
- cmd = strtok_r(command, ";", &save);
- if (cmd)
- {
- uint16_t port = DEFAULT_NETWORK_CMD_PORT;
- const char *port_ = NULL;
- const char *host = strtok_r(NULL, ";", &save);
- if (host)
- port_ = strtok_r(NULL, ";", &save);
- else
- {
- #ifdef _WIN32
- host = "127.0.0.1";
- #else
- host = "localhost";
- #endif
- }
- if (port_)
- port = strtoul(port_, NULL, 0);
- RARCH_LOG("%s: \"%s\" to %s:%hu\n",
- msg_hash_to_str(MSG_SENDING_COMMAND),
- cmd, host, (unsigned short)port);
- if (command_verify(cmd) && udp_send_packet(host, port, cmd))
- {
- free(command);
- return true;
- }
- }
- free(command);
- return false;
- }
- #endif
- #if defined(HAVE_NETWORKING) && defined(HAVE_NETWORK_CMD) && defined(HAVE_COMMAND)
- static void command_parse_sub_msg(command_t *handle, const char *tok)
- {
- const char *arg = NULL;
- unsigned index = 0;
- if (command_get_arg(tok, &arg, &index))
- {
- if (arg)
- {
- if (!action_map[index].action(arg))
- RARCH_ERR("Command \"%s\" failed.\n", arg);
- }
- else
- handle->state[map[index].id] = true;
- }
- else
- RARCH_WARN("%s \"%s\" %s.\n",
- msg_hash_to_str(MSG_UNRECOGNIZED_COMMAND),
- tok,
- msg_hash_to_str(MSG_RECEIVED));
- }
- static void command_parse_msg(
- struct rarch_state *p_rarch,
- command_t *handle,
- char *buf, enum cmd_source_t source)
- {
- char *save = NULL;
- const char *tok = strtok_r(buf, "\n", &save);
- p_rarch->lastcmd_source = source;
- while (tok)
- {
- command_parse_sub_msg(handle, tok);
- tok = strtok_r(NULL, "\n", &save);
- }
- p_rarch->lastcmd_source = CMD_NONE;
- }
- static void command_network_poll(
- struct rarch_state *p_rarch,
- command_t *handle)
- {
- fd_set fds;
- struct timeval tmp_tv = {0};
- if (handle->net_fd < 0)
- return;
- FD_ZERO(&fds);
- FD_SET(handle->net_fd, &fds);
- if (socket_select(handle->net_fd + 1, &fds, NULL, NULL, &tmp_tv) <= 0)
- return;
- if (!FD_ISSET(handle->net_fd, &fds))
- return;
- for (;;)
- {
- ssize_t ret;
- char buf[1024];
- buf[0] = '\0';
- p_rarch->lastcmd_net_fd = handle->net_fd;
- p_rarch->lastcmd_net_source_len = sizeof(p_rarch->lastcmd_net_source);
- ret = recvfrom(handle->net_fd, buf,
- sizeof(buf) - 1, 0,
- (struct sockaddr*)&p_rarch->lastcmd_net_source,
- &p_rarch->lastcmd_net_source_len);
- if (ret <= 0)
- break;
- buf[ret] = '\0';
- command_parse_msg(p_rarch, handle, buf, CMD_NETWORK);
- }
- }
- #endif
- static bool command_free(command_t *handle)
- {
- #ifdef HAVE_NETWORK_CMD
- if (handle && handle->net_fd >= 0)
- socket_close(handle->net_fd);
- #endif
- free(handle);
- return true;
- }
- #ifdef HAVE_STDIN_CMD
- static bool command_stdin_init(command_t *handle)
- {
- #ifndef _WIN32
- #ifdef HAVE_NETWORKING
- if (!socket_nonblock(STDIN_FILENO))
- return false;
- #endif
- #endif
- handle->stdin_enable = true;
- return true;
- }
- static void command_stdin_poll(
- struct rarch_state *p_rarch,
- command_t *handle)
- {
- ptrdiff_t msg_len;
- char *last_newline = NULL;
- ssize_t ret = read_stdin(
- handle->stdin_buf + handle->stdin_buf_ptr,
- STDIN_BUF_SIZE - handle->stdin_buf_ptr - 1);
- if (ret == 0)
- return;
- handle->stdin_buf_ptr += ret;
- handle->stdin_buf[handle->stdin_buf_ptr] = '\0';
- last_newline =
- strrchr(handle->stdin_buf, '\n');
- if (!last_newline)
- {
- /* We're receiving bogus data in pipe
- * (no terminating newline), flush out the buffer. */
- if (handle->stdin_buf_ptr + 1 >= STDIN_BUF_SIZE)
- {
- handle->stdin_buf_ptr = 0;
- handle->stdin_buf[0] = '\0';
- }
- return;
- }
- *last_newline++ = '\0';
- msg_len = last_newline - handle->stdin_buf;
- #if defined(HAVE_NETWORKING)
- command_parse_msg(p_rarch,
- handle, handle->stdin_buf, CMD_STDIN);
- #endif
- memmove(handle->stdin_buf, last_newline,
- handle->stdin_buf_ptr - msg_len);
- handle->stdin_buf_ptr -= msg_len;
- }
- #endif
- static bool command_network_new(
- command_t *handle,
- bool stdin_enable,
- bool network_enable,
- uint16_t port)
- {
- #ifdef HAVE_NETWORK_CMD
- handle->net_fd = -1;
- if (network_enable && !command_network_init(handle, port))
- goto error;
- #endif
- #ifdef HAVE_STDIN_CMD
- handle->stdin_enable = stdin_enable;
- if (stdin_enable && !command_stdin_init(handle))
- goto error;
- #endif
- return true;
- #if defined(HAVE_NETWORK_CMD) || defined(HAVE_STDIN_CMD)
- error:
- command_free(handle);
- return false;
- #endif
- }
- #endif
- static bool retroarch_apply_shader(
- enum rarch_shader_type type,
- const char *preset_path, bool message)
- {
- #if defined(HAVE_CG) || defined(HAVE_GLSL) || defined(HAVE_SLANG) || defined(HAVE_HLSL)
- char msg[256];
- struct rarch_state *p_rarch = &rarch_st;
- settings_t *settings = p_rarch->configuration_settings;
- const char *core_name = p_rarch->runloop_system.info.library_name;
- const char *preset_file = NULL;
- #ifdef HAVE_MENU
- struct video_shader *shader = menu_shader_get();
- #endif
- /* Disallow loading shaders when no core is loaded */
- if (string_is_empty(core_name))
- return false;
- if (!string_is_empty(preset_path))
- preset_file = path_basename(preset_path);
- p_rarch->runtime_shader_preset[0] = '\0';
- /* TODO/FIXME - This loads the shader into the video driver
- * But then we load the shader from disk twice more to put it in the menu
- * We need to reconfigure this at some point to only load it once */
- if (p_rarch->current_video->set_shader)
- {
- if ((p_rarch->current_video->set_shader(
- p_rarch->video_driver_data, type, preset_path)))
- {
- configuration_set_bool(settings, settings->bools.video_shader_enable, true);
- if (!string_is_empty(preset_path))
- {
- strlcpy(p_rarch->runtime_shader_preset, preset_path,
- sizeof(p_rarch->runtime_shader_preset));
- #ifdef HAVE_MENU
- /* reflect in shader manager */
- if (menu_shader_manager_set_preset(
- shader, type, preset_path, false))
- shader->modified = false;
- #endif
- }
- if (message)
- {
- /* Display message */
- if (preset_file)
- snprintf(msg, sizeof(msg),
- "%s: \"%s\"",
- msg_hash_to_str(MSG_SHADER),
- preset_file);
- else
- {
- strlcpy(msg, msg_hash_to_str(MSG_SHADER), sizeof(msg));
- strlcat(msg, ": ", sizeof(msg));
- strlcat(msg, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_NONE), sizeof(msg));
- }
- #ifdef HAVE_GFX_WIDGETS
- if (p_rarch->widgets_active)
- gfx_widget_set_generic_message(&p_rarch->dispwidget_st,
- msg, 2000);
- else
- #endif
- runloop_msg_queue_push(msg, 1, 120, true, NULL,
- MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO);
- }
- RARCH_LOG("%s \"%s\".\n",
- msg_hash_to_str(MSG_APPLYING_SHADER),
- preset_path ? preset_path : "null");
- return true;
- }
- }
- #ifdef HAVE_MENU
- /* reflect in shader manager */
- menu_shader_manager_set_preset(shader, type, NULL, false);
- #endif
- /* Display error message */
- fill_pathname_join_delim(msg,
- msg_hash_to_str(MSG_FAILED_TO_APPLY_SHADER_PRESET),
- preset_file ? preset_file : "null",
- ' ',
- sizeof(msg));
- runloop_msg_queue_push(
- msg, 1, 180, true, NULL,
- MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_ERROR);
- #endif
- return false;
- }
- #if defined(HAVE_CG) || defined(HAVE_GLSL) || defined(HAVE_SLANG) || defined(HAVE_HLSL)
- static bool command_set_shader(const char *arg)
- {
- enum rarch_shader_type type = video_shader_parse_type(arg);
- struct rarch_state *p_rarch = &rarch_st;
- settings_t *settings = p_rarch->configuration_settings;
- if (!string_is_empty(arg))
- {
- if (!video_shader_is_supported(type))
- return false;
- /* rebase on shader directory */
- if (!path_is_absolute(arg))
- {
- char abs_arg[PATH_MAX_LENGTH];
- const char *ref_path = settings->paths.directory_video_shader;
- fill_pathname_join(abs_arg,
- ref_path, arg, sizeof(abs_arg));
- arg = abs_arg;
- }
- }
- return retroarch_apply_shader(type, arg, true);
- }
- #endif
- /* TRANSLATION */
- #ifdef HAVE_TRANSLATE
- static bool task_auto_translate_callback(struct rarch_state *p_rarch)
- {
- bool was_paused = p_rarch->runloop_paused;
- command_event(CMD_EVENT_AI_SERVICE_CALL, &was_paused);
- return true;
- }
- /* TODO/FIXME - Doesn't currently work. Fix this. */
- static bool is_ai_service_speech_running(void)
- {
- #ifdef HAVE_AUDIOMIXER
- enum audio_mixer_state res = audio_driver_mixer_get_stream_state(10);
- bool ret = (res == AUDIO_STREAM_STATE_NONE) || (res == AUDIO_STREAM_STATE_STOPPED);
- if (!ret)
- return true;
- #endif
- return false;
- }
- static bool ai_service_speech_stop(void)
- {
- #ifdef HAVE_AUDIOMIXER
- audio_driver_mixer_stop_stream(10);
- audio_driver_mixer_remove_stream(10);
- #endif
- return false;
- }
- static void task_auto_translate_handler(retro_task_t *task)
- {
- int *mode_ptr = (int*)task->user_data;
- struct rarch_state *p_rarch = &rarch_st;
- if (task_get_cancelled(task))
- goto task_finished;
- switch (*mode_ptr)
- {
- case 1: /* Speech Mode */
- #ifdef HAVE_AUDIOMIXER
- if (!is_ai_service_speech_running())
- goto task_finished;
- #endif
- break;
- case 2: /* Narrator Mode */
- #ifdef HAVE_ACCESSIBILITY
- if (!is_narrator_running(p_rarch))
- goto task_finished;
- #endif
- break;
- default:
- break;
- }
- return;
- task_finished:
- if (p_rarch->ai_service_auto == 1)
- p_rarch->ai_service_auto = 2;
- task_set_finished(task, true);
- if (*mode_ptr == 1 || *mode_ptr == 2)
- task_auto_translate_callback(p_rarch);
- if (task->user_data)
- free(task->user_data);
- }
- static bool call_auto_translate_task(
- struct rarch_state *p_rarch,
- bool *was_paused)
- {
- settings_t *settings = p_rarch->configuration_settings;
- int ai_service_mode = settings->uints.ai_service_mode;
- /*Image Mode*/
- if (ai_service_mode == 0)
- {
- if (p_rarch->ai_service_auto == 1)
- p_rarch->ai_service_auto = 2;
- command_event(CMD_EVENT_AI_SERVICE_CALL, was_paused);
- return true;
- }
- else /* Speech or Narrator Mode */
- {
- int* mode = NULL;
- retro_task_t *t = task_init();
- if (!t)
- return false;
- mode = (int*)malloc(sizeof(int));
- *mode = ai_service_mode;
- t->handler = task_auto_translate_handler;
- t->user_data = mode;
- t->mute = true;
- task_queue_push(t);
- }
- return true;
- }
- static void handle_translation_cb(
- retro_task_t *task, void *task_data,
- void *user_data, const char *error)
- {
- size_t pitch;
- unsigned width, height;
- unsigned image_width, image_height;
- uint8_t* raw_output_data = NULL;
- char* raw_image_file_data = NULL;
- struct scaler_ctx* scaler = NULL;
- http_transfer_data_t *data = (http_transfer_data_t*)task_data;
- int new_image_size = 0;
- #ifdef HAVE_AUDIOMIXER
- int new_sound_size = 0;
- #endif
- const void* dummy_data = NULL;
- void* raw_image_data = NULL;
- void* raw_image_data_alpha = NULL;
- void* raw_sound_data = NULL;
- int retval = 0;
- rjson_t* json = NULL;
- int json_current_key = 0;
- char* err_string = NULL;
- char* text_string = NULL;
- char* auto_string = NULL;
- char* key_string = NULL;
- struct rarch_state *p_rarch = &rarch_st;
- settings_t* settings = p_rarch->configuration_settings;
- bool was_paused = p_rarch->runloop_paused;
- const enum retro_pixel_format
- video_driver_pix_fmt = p_rarch->video_driver_pix_fmt;
- #ifdef HAVE_GFX_WIDGETS
- bool gfx_widgets_paused = p_rarch->gfx_widgets_paused;
- /* When auto mode is on, we turn off the overlay
- * once we have the result for the next call.*/
- if (p_rarch->dispwidget_st.ai_service_overlay_state != 0
- && p_rarch->ai_service_auto == 2)
- gfx_widgets_ai_service_overlay_unload(&p_rarch->dispwidget_st);
- #endif
- #ifdef DEBUG
- if (p_rarch->ai_service_auto != 2)
- RARCH_LOG("RESULT FROM AI SERVICE...\n");
- #endif
- if (!data || error || !data->data)
- goto finish;
- json = rjson_open_buffer(data->data, data->len);
- if (!json)
- goto finish;
- /* Parse JSON body for the image and sound data */
- for (;;)
- {
- static const char* keys[] = { "image", "sound", "text", "error", "auto", "press" };
- const char *str = NULL;
- size_t str_len = 0;
- enum rjson_type json_type = rjson_next(json);
- if (json_type == RJSON_DONE || json_type == RJSON_ERROR)
- break;
- if (json_type != RJSON_STRING)
- continue;
- if (rjson_get_context_type(json) != RJSON_OBJECT)
- continue;
- str = rjson_get_string(json, &str_len);
- if ((rjson_get_context_count(json) & 1) == 1)
- {
- int i;
- json_current_key = -1;
- for (i = 0; i != (sizeof(keys)/sizeof(keys[0])); i++)
- {
- if (string_is_equal(str, keys[i]))
- {
- json_current_key = i;
- break;
- }
- }
- }
- else
- {
- switch (json_current_key)
- {
- case 0: /* image */
- raw_image_file_data = (char*)unbase64(str,
- (int)str_len, &new_image_size);
- break;
- #ifdef HAVE_AUDIOMIXER
- case 1: /* sound */
- raw_sound_data = (void*)unbase64(str,
- (int)str_len, &new_sound_size);
- break;
- #endif
- case 2: /* text */
- text_string = strdup(str);
- break;
- case 3: /* error */
- err_string = strdup(str);
- break;
- case 4: /* auto */
- auto_string = strdup(str);
- break;
- case 5: /* press */
- key_string = strdup(str);
- break;
- }
- json_current_key = -1;
- }
- }
- if (string_is_equal(err_string, "No text found."))
- {
- #ifdef DEBUG
- RARCH_LOG("No text found...\n");
- #endif
- if (text_string)
- {
- free(text_string);
- text_string = NULL;
- }
- text_string = (char*)malloc(15);
- strlcpy(text_string, err_string, 15);
- #ifdef HAVE_GFX_WIDGETS
- if (gfx_widgets_paused)
- {
- /* In this case we have to unpause and then repause for a frame */
- p_rarch->dispwidget_st.ai_service_overlay_state = 2;
- command_event(CMD_EVENT_UNPAUSE, NULL);
- }
- #endif
- }
- if ( !raw_image_file_data
- && !raw_sound_data
- && !text_string
- && (p_rarch->ai_service_auto != 2)
- && !key_string)
- {
- error = "Invalid JSON body.";
- goto finish;
- }
- if (raw_image_file_data)
- {
- /* Get the video frame dimensions reference */
- video_driver_cached_frame_get(&dummy_data, &width, &height, &pitch);
- /* try two different modes for text display *
- * In the first mode, we use display widget overlays, but they require
- * the video poke interface to be able to load image buffers.
- *
- * The other method is to draw to the video buffer directly, which needs
- * a software core to be running. */
- #ifdef HAVE_GFX_WIDGETS
- if (p_rarch->video_driver_poke
- && p_rarch->video_driver_poke->load_texture
- && p_rarch->video_driver_poke->unload_texture)
- {
- bool ai_res;
- enum image_type_enum image_type;
- /* Write to overlay */
- if ( raw_image_file_data[0] == 'B' &&
- raw_image_file_data[1] == 'M')
- image_type = IMAGE_TYPE_BMP;
- else if (raw_image_file_data[1] == 'P' &&
- raw_image_file_data[2] == 'N' &&
- raw_image_file_data[3] == 'G')
- image_type = IMAGE_TYPE_PNG;
- else
- {
- RARCH_LOG("Invalid image type returned from server.\n");
- goto finish;
- }
- ai_res = gfx_widgets_ai_service_overlay_load(
- &p_rarch->dispwidget_st,
- raw_image_file_data, (unsigned)new_image_size,
- image_type);
- if (!ai_res)
- {
- RARCH_LOG("Video driver not supported for AI Service.");
- runloop_msg_queue_push(
- /* msg_hash_to_str(MSG_VIDEO_DRIVER_NOT_SUPPORTED), */
- "Video driver not supported.",
- 1, 180, true,
- NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO);
- }
- else if (gfx_widgets_paused)
- {
- /* In this case we have to unpause and then repause for a frame */
- #ifdef HAVE_TRANSLATE
- /* Unpausing state */
- p_rarch->dispwidget_st.ai_service_overlay_state = 2;
- #endif
- command_event(CMD_EVENT_UNPAUSE, NULL);
- }
- }
- else
- #endif
- /* Can't use display widget overlays, so try writing to video buffer */
- {
- /* Write to video buffer directly (software cores only) */
- if (raw_image_file_data[0] == 'B' && raw_image_file_data[1] == 'M')
- {
- /* This is a BMP file coming back. */
- /* Get image data (24 bit), and convert to the emulated pixel format */
- image_width =
- ((uint32_t) ((uint8_t)raw_image_file_data[21]) << 24) +
- ((uint32_t) ((uint8_t)raw_image_file_data[20]) << 16) +
- ((uint32_t) ((uint8_t)raw_image_file_data[19]) << 8) +
- ((uint32_t) ((uint8_t)raw_image_file_data[18]) << 0);
- image_height =
- ((uint32_t) ((uint8_t)raw_image_file_data[25]) << 24) +
- ((uint32_t) ((uint8_t)raw_image_file_data[24]) << 16) +
- ((uint32_t) ((uint8_t)raw_image_file_data[23]) << 8) +
- ((uint32_t) ((uint8_t)raw_image_file_data[22]) << 0);
- raw_image_data = (void*)malloc(image_width*image_height*3*sizeof(uint8_t));
- memcpy(raw_image_data,
- raw_image_file_data+54*sizeof(uint8_t),
- image_width*image_height*3*sizeof(uint8_t));
- }
- else if (raw_image_file_data[1] == 'P' && raw_image_file_data[2] == 'N' &&
- raw_image_file_data[3] == 'G')
- {
- rpng_t *rpng = NULL;
- /* PNG coming back from the url */
- image_width =
- ((uint32_t) ((uint8_t)raw_image_file_data[16]) << 24)+
- ((uint32_t) ((uint8_t)raw_image_file_data[17]) << 16)+
- ((uint32_t) ((uint8_t)raw_image_file_data[18]) << 8)+
- ((uint32_t) ((uint8_t)raw_image_file_data[19]) << 0);
- image_height =
- ((uint32_t) ((uint8_t)raw_image_file_data[20]) << 24)+
- ((uint32_t) ((uint8_t)raw_image_file_data[21]) << 16)+
- ((uint32_t) ((uint8_t)raw_image_file_data[22]) << 8)+
- ((uint32_t) ((uint8_t)raw_image_file_data[23]) << 0);
- rpng = rpng_alloc();
- if (!rpng)
- {
- error = "Can't allocate memory.";
- goto finish;
- }
- rpng_set_buf_ptr(rpng, raw_image_file_data, (size_t)new_image_size);
- rpng_start(rpng);
- while (rpng_iterate_image(rpng));
- do
- {
- retval = rpng_process_image(rpng, &raw_image_data_alpha,
- (size_t)new_image_size, &image_width, &image_height);
- } while (retval == IMAGE_PROCESS_NEXT);
- /* Returned output from the png processor is an upside down RGBA
- * image, so we have to change that to RGB first. This should
- * probably be replaced with a scaler call.*/
- {
- unsigned ui;
- int d,tw,th,tc;
- d=0;
- raw_image_data = (void*)malloc(image_width*image_height*3*sizeof(uint8_t));
- for (ui = 0; ui < image_width * image_height * 4; ui++)
- {
- if (ui % 4 != 3)
- {
- tc = d%3;
- th = image_height-d / (3*image_width)-1;
- tw = (d%(image_width*3)) / 3;
- ((uint8_t*) raw_image_data)[tw*3+th*3*image_width+tc] = ((uint8_t *)raw_image_data_alpha)[ui];
- d+=1;
- }
- }
- }
- rpng_free(rpng);
- }
- else
- {
- RARCH_LOG("Output from URL not a valid file type, or is not supported.\n");
- goto finish;
- }
- scaler = (struct scaler_ctx*)calloc(1, sizeof(struct scaler_ctx));
- if (!scaler)
- goto finish;
- if (dummy_data == RETRO_HW_FRAME_BUFFER_VALID)
- {
- /*
- In this case, we used the viewport to grab the image
- and translate it, and we have the translated image in
- the raw_image_data buffer.
- */
- RARCH_LOG("Hardware frame buffer core, but selected video driver isn't supported.\n");
- goto finish;
- }
- /* The assigned pitch may not be reliable. The width of
- the video frame can change during run-time, but the
- pitch may not, so we just assign it as the width
- times the byte depth.
- */
- if (video_driver_pix_fmt == RETRO_PIXEL_FORMAT_XRGB8888)
- {
- raw_output_data = (uint8_t*)malloc(width * height * 4 * sizeof(uint8_t));
- scaler->out_fmt = SCALER_FMT_ARGB8888;
- pitch = width * 4;
- scaler->out_stride = width * 4;
- }
- else
- {
- raw_output_data = (uint8_t*)malloc(width * height * 2 * sizeof(uint8_t));
- scaler->out_fmt = SCALER_FMT_RGB565;
- pitch = width * 2;
- scaler->out_stride = width * 1;
- }
- if (!raw_output_data)
- goto finish;
- scaler->in_fmt = SCALER_FMT_BGR24;
- scaler->in_width = image_width;
- scaler->in_height = image_height;
- scaler->out_width = width;
- scaler->out_height = height;
- scaler->scaler_type = SCALER_TYPE_POINT;
- scaler_ctx_gen_filter(scaler);
- scaler->in_stride = -1 * width * 3;
- scaler_ctx_scale_direct(scaler, raw_output_data,
- (uint8_t*)raw_image_data + (image_height - 1) * width * 3);
- video_driver_frame(raw_output_data, image_width, image_height, pitch);
- }
- }
- #ifdef HAVE_AUDIOMIXER
- if (raw_sound_data)
- {
- audio_mixer_stream_params_t params;
- params.volume = 1.0f;
- params.slot_selection_type = AUDIO_MIXER_SLOT_SELECTION_MANUAL; /* user->slot_selection_type; */
- params.slot_selection_idx = 10;
- params.stream_type = AUDIO_STREAM_TYPE_SYSTEM; /* user->stream_type; */
- params.type = AUDIO_MIXER_TYPE_WAV;
- params.state = AUDIO_STREAM_STATE_PLAYING;
- params.buf = raw_sound_data;
- params.bufsize = new_sound_size;
- params.cb = NULL;
- params.basename = NULL;
- audio_driver_mixer_add_stream(¶ms);
- if (raw_sound_data)
- {
- free(raw_sound_data);
- raw_sound_data = NULL;
- }
- }
- #endif
- if (key_string)
- {
- char key[8];
- size_t length = strlen(key_string);
- int i = 0;
- int start = 0;
- char t = ' ';
- for (i = 1; i < (int)length; i++)
- {
- t = key_string[i];
- if (i == length-1 || t == ' ' || t == ',')
- {
- if (i == length-1 && t != ' ' && t!= ',')
- i++;
- if (i-start > 7)
- {
- start = i;
- continue;
- }
- strncpy(key, key_string+start, i-start);
- key[i-start] = '\0';
- #ifdef HAVE_ACCESSIBILITY
- #ifdef HAVE_TRANSLATE
- if (string_is_equal(key, "b"))
- p_rarch->ai_gamepad_state[0] = 2;
- if (string_is_equal(key, "y"))
- p_rarch->ai_gamepad_state[1] = 2;
- if (string_is_equal(key, "select"))
- p_rarch->ai_gamepad_state[2] = 2;
- if (string_is_equal(key, "start"))
- p_rarch->ai_gamepad_state[3] = 2;
- if (string_is_equal(key, "up"))
- p_rarch->ai_gamepad_state[4] = 2;
- if (string_is_equal(key, "down"))
- p_rarch->ai_gamepad_state[5] = 2;
- if (string_is_equal(key, "left"))
- p_rarch->ai_gamepad_state[6] = 2;
- if (string_is_equal(key, "right"))
- p_rarch->ai_gamepad_state[7] = 2;
- if (string_is_equal(key, "a"))
- p_rarch->ai_gamepad_state[8] = 2;
- if (string_is_equal(key, "x"))
- p_rarch->ai_gamepad_state[9] = 2;
- if (string_is_equal(key, "l"))
- p_rarch->ai_gamepad_state[10] = 2;
- if (string_is_equal(key, "r"))
- p_rarch->ai_gamepad_state[11] = 2;
- if (string_is_equal(key, "l2"))
- p_rarch->ai_gamepad_state[12] = 2;
- if (string_is_equal(key, "r2"))
- p_rarch->ai_gamepad_state[13] = 2;
- if (string_is_equal(key, "l3"))
- p_rarch->ai_gamepad_state[14] = 2;
- if (string_is_equal(key, "r3"))
- p_rarch->ai_gamepad_state[15] = 2;
- #endif
- #endif
- if (string_is_equal(key, "pause"))
- command_event(CMD_EVENT_PAUSE, NULL);
- if (string_is_equal(key, "unpause"))
- command_event(CMD_EVENT_UNPAUSE, NULL);
- start = i+1;
- }
- }
- }
- #ifdef HAVE_ACCESSIBILITY
- if (text_string && is_accessibility_enabled(p_rarch))
- accessibility_speak_priority(p_rarch, text_string, 10);
- #endif
- finish:
- if (error)
- RARCH_ERR("%s: %s\n", msg_hash_to_str(MSG_DOWNLOAD_FAILED), error);
- if (data)
- {
- if (data->data)
- free(data->data);
- free(data);
- }
- if (user_data)
- free(user_data);
- if (json)
- rjson_free(json);
- if (raw_image_file_data)
- free(raw_image_file_data);
- if (raw_image_data_alpha)
- free(raw_image_data_alpha);
- if (raw_image_data)
- free(raw_image_data);
- if (scaler)
- free(scaler);
- if (err_string)
- free(err_string);
- if (text_string)
- free(text_string);
- if (raw_output_data)
- free(raw_output_data);
- if (string_is_equal(auto_string, "auto"))
- {
- if ( (p_rarch->ai_service_auto != 0)
- && !settings->bools.ai_service_pause)
- call_auto_translate_task(p_rarch, &was_paused);
- }
- if (auto_string)
- free(auto_string);
- if (key_string)
- free(key_string);
- }
- static const char *ai_service_get_str(enum translation_lang id)
- {
- switch (id)
- {
- case TRANSLATION_LANG_EN:
- return "en";
- case TRANSLATION_LANG_ES:
- return "es";
- case TRANSLATION_LANG_FR:
- return "fr";
- case TRANSLATION_LANG_IT:
- return "it";
- case TRANSLATION_LANG_DE:
- return "de";
- case TRANSLATION_LANG_JP:
- return "ja";
- case TRANSLATION_LANG_NL:
- return "nl";
- case TRANSLATION_LANG_CS:
- return "cs";
- case TRANSLATION_LANG_DA:
- return "da";
- case TRANSLATION_LANG_SV:
- return "sv";
- case TRANSLATION_LANG_HR:
- return "hr";
- case TRANSLATION_LANG_KO:
- return "ko";
- case TRANSLATION_LANG_ZH_CN:
- return "zh-CN";
- case TRANSLATION_LANG_ZH_TW:
- return "zh-TW";
- case TRANSLATION_LANG_CA:
- return "ca";
- case TRANSLATION_LANG_BG:
- return "bg";
- case TRANSLATION_LANG_BN:
- return "bn";
- case TRANSLATION_LANG_EU:
- return "eu";
- case TRANSLATION_LANG_AZ:
- return "az";
- case TRANSLATION_LANG_AR:
- return "ar";
- case TRANSLATION_LANG_AST:
- return "ast";
- case TRANSLATION_LANG_SQ:
- return "sq";
- case TRANSLATION_LANG_AF:
- return "af";
- case TRANSLATION_LANG_EO:
- return "eo";
- case TRANSLATION_LANG_ET:
- return "et";
- case TRANSLATION_LANG_TL:
- return "tl";
- case TRANSLATION_LANG_FI:
- return "fi";
- case TRANSLATION_LANG_GL:
- return "gl";
- case TRANSLATION_LANG_KA:
- return "ka";
- case TRANSLATION_LANG_EL:
- return "el";
- case TRANSLATION_LANG_GU:
- return "gu";
- case TRANSLATION_LANG_HT:
- return "ht";
- case TRANSLATION_LANG_HE:
- return "he";
- case TRANSLATION_LANG_HI:
- return "hi";
- case TRANSLATION_LANG_HU:
- return "hu";
- case TRANSLATION_LANG_IS:
- return "is";
- case TRANSLATION_LANG_ID:
- return "id";
- case TRANSLATION_LANG_GA:
- return "ga";
- case TRANSLATION_LANG_KN:
- return "kn";
- case TRANSLATION_LANG_LA:
- return "la";
- case TRANSLATION_LANG_LV:
- return "lv";
- case TRANSLATION_LANG_LT:
- return "lt";
- case TRANSLATION_LANG_MK:
- return "mk";
- case TRANSLATION_LANG_MS:
- return "ms";
- case TRANSLATION_LANG_MT:
- return "mt";
- case TRANSLATION_LANG_NO:
- return "no";
- case TRANSLATION_LANG_FA:
- return "fa";
- case TRANSLATION_LANG_PL:
- return "pl";
- case TRANSLATION_LANG_PT:
- return "pt";
- case TRANSLATION_LANG_RO:
- return "ro";
- case TRANSLATION_LANG_RU:
- return "ru";
- case TRANSLATION_LANG_SR:
- return "sr";
- case TRANSLATION_LANG_SK:
- return "sk";
- case TRANSLATION_LANG_SL:
- return "sl";
- case TRANSLATION_LANG_SW:
- return "sw";
- case TRANSLATION_LANG_TA:
- return "ta";
- case TRANSLATION_LANG_TE:
- return "te";
- case TRANSLATION_LANG_TH:
- return "th";
- case TRANSLATION_LANG_TR:
- return "tr";
- case TRANSLATION_LANG_UK:
- return "uk";
- case TRANSLATION_LANG_UR:
- return "ur";
- case TRANSLATION_LANG_VI:
- return "vi";
- case TRANSLATION_LANG_CY:
- return "cy";
- case TRANSLATION_LANG_YI:
- return "yi";
- case TRANSLATION_LANG_DONT_CARE:
- case TRANSLATION_LANG_LAST:
- break;
- }
- return "";
- }
- /*
- This function does all the stuff needed to translate the game screen,
- using the URL given in the settings. Once the image from the frame
- buffer is sent to the server, the callback will write the translated
- image to the screen.
- Supported client/services (thus far)
- -VGTranslate client ( www.gitlab.com/spherebeaker/vg_translate )
- -Ztranslate client/service ( www.ztranslate.net/docs/service )
- To use a client, download the relevant code/release, configure
- them, and run them on your local machine, or network. Set the
- retroarch configuration to point to your local client (usually
- listening on localhost:4404 ) and enable translation service.
- If you don't want to run a client, you can also use a service,
- which is basically like someone running a client for you. The
- downside here is that your retroarch device will have to have
- an internet connection, and you may have to sign up for it.
- To make your own server, it must listen for a POST request, which
- will consist of a JSON body, with the "image" field as a base64
- encoded string of a 24bit-BMP/PNG that the will be translated.
- The server must output the translated image in the form of a
- JSON body, with the "image" field also as a base64 encoded
- 24bit-BMP, or as an alpha channel png.
- "paused" boolean is passed in to indicate if the current call
- was made during a paused frame. Due to how the menu widgets work,
- if the ai service is called in "auto" mode, then this call will
- be made while the menu widgets unpause the core for a frame to update
- the on-screen widgets. To tell the ai service what the pause
- mode is honestly, we store the runloop_paused variable from before
- the handle_translation_cb wipes the widgets, and pass that in here.
- */
- static bool run_translation_service(
- settings_t *settings,
- struct rarch_state *p_rarch,
- bool paused)
- {
- struct video_viewport vp;
- uint8_t header[54];
- size_t pitch;
- unsigned width, height;
- const void *data = NULL;
- uint8_t *bit24_image = NULL;
- uint8_t *bit24_image_prev = NULL;
- struct scaler_ctx *scaler = (struct scaler_ctx*)
- calloc(1, sizeof(struct scaler_ctx));
- bool error = false;
- uint8_t *bmp_buffer = NULL;
- uint64_t buffer_bytes = 0;
- char *bmp64_buffer = NULL;
- rjsonwriter_t* jsonwriter = NULL;
- const char *json_buffer = NULL;
- int bmp64_length = 0;
- bool TRANSLATE_USE_BMP = false;
- bool use_overlay = false;
- const char *label = NULL;
- char* system_label = NULL;
- core_info_t *core_info = NULL;
- const enum retro_pixel_format
- video_driver_pix_fmt = p_rarch->video_driver_pix_fmt;
- #ifdef HAVE_GFX_WIDGETS
- /* For the case when ai service pause is disabled. */
- if ( (p_rarch->dispwidget_st.ai_service_overlay_state != 0)
- && (p_rarch->ai_service_auto == 1))
- {
- gfx_widgets_ai_service_overlay_unload(&p_rarch->dispwidget_st);
- goto finish;
- }
- #endif
- #ifdef HAVE_GFX_WIDGETS
- if ( p_rarch->video_driver_poke
- && p_rarch->video_driver_poke->load_texture
- && p_rarch->video_driver_poke->unload_texture)
- use_overlay = true;
- #endif
- /* get the core info here so we can pass long the game name */
- core_info_get_current_core(&core_info);
- if (core_info)
- {
- size_t label_len;
- const char *system_id = core_info->system_id
- ? core_info->system_id : "core";
- size_t system_id_len = strlen(system_id);
- const struct playlist_entry *entry = NULL;
- playlist_t *current_playlist = playlist_get_cached();
- if (current_playlist)
- {
- playlist_get_index_by_path(
- current_playlist, path_get(RARCH_PATH_CONTENT), &entry);
- if (entry && !string_is_empty(entry->label))
- label = entry->label;
- }
- if (!label)
- label = path_basename(path_get(RARCH_PATH_BASENAME));
- label_len = strlen(label);
- system_label = (char*)malloc(label_len + system_id_len + 3);
- memcpy(system_label, system_id, system_id_len);
- memcpy(system_label + system_id_len, "__", 2);
- memcpy(system_label + 2 + system_id_len, label, label_len);
- system_label[system_id_len + 2 + label_len] = '\0';
- }
- if (!scaler)
- goto finish;
- video_driver_cached_frame_get(&data, &width, &height, &pitch);
- if (!data)
- goto finish;
- if (data == RETRO_HW_FRAME_BUFFER_VALID)
- {
- /*
- The direct frame capture didn't work, so try getting it
- from the viewport instead. This isn't as good as the
- raw frame buffer, since the viewport may us bilinear
- filtering, or other shaders that will completely trash
- the OCR, but it's better than nothing.
- */
- vp.x = 0;
- vp.y = 0;
- vp.width = 0;
- vp.height = 0;
- vp.full_width = 0;
- vp.full_height = 0;
- video_driver_get_viewport_info(&vp);
- if (!vp.width || !vp.height)
- goto finish;
- bit24_image_prev = (uint8_t*)malloc(vp.width * vp.height * 3);
- bit24_image = (uint8_t*)malloc(width * height * 3);
- if (!bit24_image_prev || !bit24_image)
- goto finish;
- if (!video_driver_read_viewport(bit24_image_prev, false))
- {
- RARCH_LOG("Could not read viewport for translation service...\n");
- goto finish;
- }
- /* TODO: Rescale down to regular resolution */
- scaler->in_fmt = SCALER_FMT_BGR24;
- scaler->out_fmt = SCALER_FMT_BGR24;
- scaler->scaler_type = SCALER_TYPE_POINT;
- scaler->in_width = vp.width;
- scaler->in_height = vp.height;
- scaler->out_width = width;
- scaler->out_height = height;
- scaler_ctx_gen_filter(scaler);
- scaler->in_stride = vp.width*3;
- scaler->out_stride = width*3;
- scaler_ctx_scale_direct(scaler, bit24_image, bit24_image_prev);
- }
- else
- {
- /* This is a software core, so just change the pixel format to 24-bit. */
- bit24_image = (uint8_t*)malloc(width * height * 3);
- if (!bit24_image)
- goto finish;
- if (video_driver_pix_fmt == RETRO_PIXEL_FORMAT_XRGB8888)
- scaler->in_fmt = SCALER_FMT_ARGB8888;
- else
- scaler->in_fmt = SCALER_FMT_RGB565;
- video_frame_convert_to_bgr24(
- scaler,
- (uint8_t *)bit24_image,
- (const uint8_t*)data + ((int)height - 1)*pitch,
- width, height,
- (int)-pitch);
- }
- scaler_ctx_gen_reset(scaler);
- if (!bit24_image)
- {
- error = true;
- goto finish;
- }
- if (TRANSLATE_USE_BMP)
- {
- /*
- At this point, we should have a screenshot in the buffer,
- so allocate an array to contain the BMP image along with
- the BMP header as bytes, and then covert that to a
- b64 encoded array for transport in JSON.
- */
- form_bmp_header(header, width, height, false);
- bmp_buffer = (uint8_t*)malloc(width * height * 3 + 54);
- if (!bmp_buffer)
- goto finish;
- memcpy(bmp_buffer, header, 54 * sizeof(uint8_t));
- memcpy(bmp_buffer + 54,
- bit24_image,
- width * height * 3 * sizeof(uint8_t));
- buffer_bytes = sizeof(uint8_t) * (width * height * 3 + 54);
- }
- else
- {
- pitch = width * 3;
- bmp_buffer = rpng_save_image_bgr24_string(
- bit24_image + width * (height-1) * 3,
- width, height, (signed)-pitch, &buffer_bytes);
- }
- bmp64_buffer = base64((void *)bmp_buffer,
- sizeof(uint8_t) * buffer_bytes,
- &bmp64_length);
- if (!bmp64_buffer)
- goto finish;
- jsonwriter = rjsonwriter_open_memory();
- if (!jsonwriter)
- goto finish;
- rjsonwriter_add_start_object(jsonwriter);
- rjsonwriter_add_space(jsonwriter);
- rjsonwriter_add_string(jsonwriter, "image");
- rjsonwriter_add_colon(jsonwriter);
- rjsonwriter_add_space(jsonwriter);
- rjsonwriter_add_string_len(jsonwriter, bmp64_buffer, bmp64_length);
- /* Form request... */
- if (system_label)
- {
- rjsonwriter_add_comma(jsonwriter);
- rjsonwriter_add_space(jsonwriter);
- rjsonwriter_add_string(jsonwriter, "label");
- rjsonwriter_add_colon(jsonwriter);
- rjsonwriter_add_space(jsonwriter);
- rjsonwriter_add_string(jsonwriter, system_label);
- }
- rjsonwriter_add_comma(jsonwriter);
- rjsonwriter_add_space(jsonwriter);
- rjsonwriter_add_string(jsonwriter, "state");
- rjsonwriter_add_colon(jsonwriter);
- rjsonwriter_add_space(jsonwriter);
- rjsonwriter_add_start_object(jsonwriter);
- rjsonwriter_add_space(jsonwriter);
- rjsonwriter_add_string(jsonwriter, "paused");
- rjsonwriter_add_colon(jsonwriter);
- rjsonwriter_add_space(jsonwriter);
- rjsonwriter_add_unsigned(jsonwriter, (paused ? 1 : 0));
- {
- static const char* state_labels[] = { "b", "y", "select", "start", "up", "down", "left", "right", "a", "x", "l", "r", "l2", "r2", "l3", "r3" };
- int i;
- for (i = 0; i != (sizeof(state_labels)/sizeof(state_labels[0])); i++)
- {
- rjsonwriter_add_comma(jsonwriter);
- rjsonwriter_add_space(jsonwriter);
- rjsonwriter_add_string(jsonwriter, state_labels[i]);
- rjsonwriter_add_colon(jsonwriter);
- rjsonwriter_add_space(jsonwriter);
- rjsonwriter_add_unsigned(jsonwriter,
- #ifdef HAVE_ACCESSIBILITY
- (p_rarch->ai_gamepad_state[i] ? 1 : 0)
- #else
- 0
- #endif
- );
- }
- }
- rjsonwriter_add_space(jsonwriter);
- rjsonwriter_add_end_object(jsonwriter);
- rjsonwriter_add_space(jsonwriter);
- rjsonwriter_add_end_object(jsonwriter);
- json_buffer = rjsonwriter_get_memory_buffer(jsonwriter, NULL);
- if (!json_buffer)
- goto finish; /* ran out of memory */
- #ifdef DEBUG
- if (p_rarch->ai_service_auto != 2)
- RARCH_LOG("Request size: %d\n", bmp64_length);
- #endif
- {
- char separator = '?';
- char new_ai_service_url[PATH_MAX_LENGTH];
- unsigned ai_service_source_lang = settings->uints.ai_service_source_lang;
- unsigned ai_service_target_lang = settings->uints.ai_service_target_lang;
- const char *ai_service_url = settings->arrays.ai_service_url;
- strlcpy(new_ai_service_url, ai_service_url, sizeof(new_ai_service_url));
- /* if query already exists in url, then use &'s instead */
- if (strrchr(new_ai_service_url, '?'))
- separator = '&';
- /* source lang */
- if (ai_service_source_lang != TRANSLATION_LANG_DONT_CARE)
- {
- const char *lang_source = ai_service_get_str(
- (enum translation_lang)ai_service_source_lang);
- if (!string_is_empty(lang_source))
- {
- char temp_string[PATH_MAX_LENGTH];
- snprintf(temp_string,
- sizeof(temp_string),
- "%csource_lang=%s", separator, lang_source);
- separator = '&';
- strlcat(new_ai_service_url,
- temp_string, sizeof(new_ai_service_url));
- }
- }
- /* target lang */
- if (ai_service_target_lang != TRANSLATION_LANG_DONT_CARE)
- {
- const char *lang_target = ai_service_get_str(
- (enum translation_lang)ai_service_target_lang);
- if (!string_is_empty(lang_target))
- {
- char temp_string[PATH_MAX_LENGTH];
- snprintf(temp_string,
- sizeof(temp_string),
- "%ctarget_lang=%s", separator, lang_target);
- separator = '&';
- strlcat(new_ai_service_url, temp_string,
- sizeof(new_ai_service_url));
- }
- }
- /* mode */
- {
- char temp_string[PATH_MAX_LENGTH];
- const char *mode_chr = NULL;
- unsigned ai_service_mode = settings->uints.ai_service_mode;
- /*"image" is included for backwards compatability with
- * vgtranslate < 1.04 */
- temp_string[0] = '\0';
- switch (ai_service_mode)
- {
- case 0:
- if (use_overlay)
- mode_chr = "image,png,png-a";
- else
- mode_chr = "image,png";
- break;
- case 1:
- mode_chr = "sound,wav";
- break;
- case 2:
- mode_chr = "text";
- break;
- case 3:
- if (use_overlay)
- mode_chr = "image,png,png-a,sound,wav";
- else
- mode_chr = "image,png,sound,wav";
- break;
- default:
- break;
- }
- snprintf(temp_string,
- sizeof(temp_string),
- "%coutput=%s", separator, mode_chr);
- separator = '&';
- strlcat(new_ai_service_url, temp_string,
- sizeof(new_ai_service_url));
- }
- #ifdef DEBUG
- if (p_rarch->ai_service_auto != 2)
- RARCH_LOG("SENDING... %s\n", new_ai_service_url);
- #endif
- task_push_http_post_transfer(new_ai_service_url,
- json_buffer, true, NULL, handle_translation_cb, NULL);
- }
- error = false;
- finish:
- if (bit24_image_prev)
- free(bit24_image_prev);
- if (bit24_image)
- free(bit24_image);
- if (scaler)
- free(scaler);
- if (bmp_buffer)
- free(bmp_buffer);
- if (bmp64_buffer)
- free(bmp64_buffer);
- if (system_label)
- free(system_label);
- if (jsonwriter)
- rjsonwriter_free(jsonwriter);
- return !error;
- }
- #endif
- /**
- * command_event_disk_control_append_image:
- * @path : Path to disk image.
- *
- * Appends disk image to disk image list.
- **/
- static bool command_event_disk_control_append_image(
- struct rarch_state *p_rarch,
- const char *path)
- {
- rarch_system_info_t *sys_info = &p_rarch->runloop_system;
- if (!sys_info)
- return false;
- if (!disk_control_append_image(&sys_info->disk_control, path))
- return false;
- #ifdef HAVE_THREADS
- retroarch_autosave_deinit(p_rarch);
- #endif
- /* TODO: Need to figure out what to do with subsystems case. */
- if (path_is_empty(RARCH_PATH_SUBSYSTEM))
- {
- /* Update paths for our new image.
- * If we actually use append_image, we assume that we
- * started out in a single disk case, and that this way
- * of doing it makes the most sense. */
- path_set(RARCH_PATH_NAMES, path);
- path_fill_names(p_rarch);
- }
- command_event(CMD_EVENT_AUTOSAVE_INIT, NULL);
- return true;
- }
- /**
- * event_set_volume:
- * @gain : amount of gain to be applied to current volume level.
- *
- * Adjusts the current audio volume level.
- *
- **/
- static void command_event_set_volume(
- settings_t *settings,
- struct rarch_state *p_rarch, float gain)
- {
- char msg[128];
- float new_volume = settings->floats.audio_volume + gain;
- new_volume = MAX(new_volume, -80.0f);
- new_volume = MIN(new_volume, 12.0f);
- configuration_set_float(settings, settings->floats.audio_volume, new_volume);
- snprintf(msg, sizeof(msg), "%s: %.1f dB",
- msg_hash_to_str(MSG_AUDIO_VOLUME),
- new_volume);
- #if defined(HAVE_GFX_WIDGETS)
- if (p_rarch->widgets_active)
- gfx_widget_volume_update_and_show(new_volume,
- p_rarch->audio_driver_mute_enable);
- else
- #endif
- runloop_msg_queue_push(msg, 1, 180, true, NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO);
- RARCH_LOG("[Audio]: %s\n", msg);
- audio_set_float(AUDIO_ACTION_VOLUME_GAIN, new_volume);
- }
- /**
- * event_set_mixer_volume:
- * @gain : amount of gain to be applied to current volume level.
- *
- * Adjusts the current audio volume level.
- *
- **/
- static void command_event_set_mixer_volume(
- settings_t *settings,
- struct rarch_state *p_rarch,
- float gain)
- {
- char msg[128];
- float new_volume = settings->floats.audio_mixer_volume + gain;
- new_volume = MAX(new_volume, -80.0f);
- new_volume = MIN(new_volume, 12.0f);
- configuration_set_float(settings, settings->floats.audio_mixer_volume, new_volume);
- snprintf(msg, sizeof(msg), "%s: %.1f dB",
- msg_hash_to_str(MSG_AUDIO_VOLUME),
- new_volume);
- runloop_msg_queue_push(msg, 1, 180, true, NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO);
- RARCH_LOG("[Audio]: %s\n", msg);
- audio_set_float(AUDIO_ACTION_VOLUME_GAIN, new_volume);
- }
- /**
- * command_event_init_controllers:
- *
- * Initialize libretro controllers.
- **/
- static void command_event_init_controllers(struct rarch_state *p_rarch)
- {
- unsigned i;
- rarch_system_info_t *info = &p_rarch->runloop_system;
- unsigned num_active_users = p_rarch->input_driver_max_users;
- unsigned ports_size = info->ports.size;
- if (!info)
- return;
- for (i = 0; i < MAX_USERS; i++)
- {
- retro_ctx_controller_info_t pad;
- const struct retro_controller_description *desc = NULL;
- unsigned device = (i < num_active_users)
- ? input_config_get_device(i)
- : RETRO_DEVICE_NONE;
- if (i >= ports_size)
- break;
- desc = libretro_find_controller_description(
- &info->ports.data[i], device);
- if (desc && !desc->desc)
- {
- /* If we're trying to connect a completely unknown device,
- * revert back to JOYPAD. */
- if (device != RETRO_DEVICE_JOYPAD && device != RETRO_DEVICE_NONE)
- {
- /* Do not fix device,
- * because any use of dummy core will reset this,
- * which is not a good idea. */
- RARCH_WARN("[Input]: Input device ID %u is unknown to this "
- "libretro implementation. Using RETRO_DEVICE_JOYPAD.\n",
- device);
- device = RETRO_DEVICE_JOYPAD;
- }
- }
- pad.device = device;
- pad.port = i;
- switch (device)
- {
- case RETRO_DEVICE_JOYPAD:
- /* Ideally these checks shouldn't be required but if we always
- * call core_set_controller_port_device input won't work on
- * cores that don't set port information properly */
- if (ports_size != 0)
- core_set_controller_port_device(&pad);
- break;
- case RETRO_DEVICE_NONE:
- default:
- /* Some cores do not properly range check port argument.
- * This is broken behavior of course, but avoid breaking
- * cores needlessly. */
- core_set_controller_port_device(&pad);
- break;
- }
- }
- }
- #ifdef HAVE_CONFIGFILE
- static void command_event_disable_overrides(struct rarch_state *p_rarch)
- {
- /* reload the original config */
- config_unload_override();
- p_rarch->runloop_overrides_active = false;
- }
- #endif
- static void command_event_deinit_core(
- struct rarch_state *p_rarch,
- bool reinit)
- {
- core_unload_game(p_rarch);
- video_driver_set_cached_frame_ptr(NULL);
- if (p_rarch->current_core.inited)
- {
- RARCH_LOG("[Core]: Unloading core..\n");
- p_rarch->current_core.retro_deinit();
- }
- RARCH_LOG("[Core]: Unloading core symbols..\n");
- uninit_libretro_symbols(p_rarch, &p_rarch->current_core);
- p_rarch->current_core.symbols_inited = false;
- if (reinit)
- driver_uninit(p_rarch, DRIVERS_CMD_ALL);
- #ifdef HAVE_CONFIGFILE
- if (p_rarch->runloop_overrides_active)
- command_event_disable_overrides(p_rarch);
- #endif
- #if defined(HAVE_CG) || defined(HAVE_GLSL) || defined(HAVE_SLANG) || defined(HAVE_HLSL)
- p_rarch->runtime_shader_preset[0] = '\0';
- #endif
- #ifdef HAVE_CONFIGFILE
- if ( p_rarch->runloop_remaps_core_active
- || p_rarch->runloop_remaps_content_dir_active
- || p_rarch->runloop_remaps_game_active
- )
- input_remapping_set_defaults(true);
- #endif
- }
- #ifdef HAVE_CHEATS
- static void command_event_init_cheats(
- settings_t *settings,
- struct rarch_state *p_rarch)
- {
- bool allow_cheats = true;
- bool apply_cheats_after_load = settings->bools.apply_cheats_after_load;
- const char *path_cheat_db = settings->paths.path_cheat_database;
- #ifdef HAVE_NETWORKING
- allow_cheats &= !netplay_driver_ctl(
- RARCH_NETPLAY_CTL_IS_DATA_INITED, NULL);
- #endif
- #ifdef HAVE_BSV_MOVIE
- allow_cheats &= !(p_rarch->bsv_movie_state_handle != NULL);
- #endif
- if (!allow_cheats)
- return;
- cheat_manager_alloc_if_empty();
- cheat_manager_load_game_specific_cheats(path_cheat_db);
- if (apply_cheats_after_load)
- cheat_manager_apply_cheats();
- }
- #endif
- static void command_event_load_auto_state(
- settings_t *settings,
- global_t *global,
- struct rarch_state *p_rarch)
- {
- char savestate_name_auto[PATH_MAX_LENGTH];
- bool ret = false;
- bool savestate_auto_load = settings->bools.savestate_auto_load;
- if (!global || !savestate_auto_load)
- return;
- #ifdef HAVE_CHEEVOS
- if (rcheevos_hardcore_active())
- return;
- #endif
- #ifdef HAVE_NETWORKING
- if (netplay_driver_ctl(RARCH_NETPLAY_CTL_IS_ENABLED, NULL))
- return;
- #endif
- savestate_name_auto[0] = '\0';
- fill_pathname_noext(savestate_name_auto, global->name.savestate,
- ".auto", sizeof(savestate_name_auto));
- if (!path_is_valid(savestate_name_auto))
- return;
- ret = content_load_state(savestate_name_auto, false, true);
- RARCH_LOG("%s: %s\n%s \"%s\" %s.\n",
- msg_hash_to_str(MSG_FOUND_AUTO_SAVESTATE_IN),
- savestate_name_auto,
- msg_hash_to_str(MSG_AUTOLOADING_SAVESTATE_FROM),
- savestate_name_auto, ret ? "succeeded" : "failed"
- );
- }
- static void command_event_set_savestate_auto_index(
- settings_t *settings,
- const global_t *global,
- struct rarch_state *p_rarch)
- {
- size_t i;
- char state_dir[PATH_MAX_LENGTH];
- char state_base[PATH_MAX_LENGTH];
- struct string_list *dir_list = NULL;
- unsigned max_idx = 0;
- bool savestate_auto_index = settings->bools.savestate_auto_index;
- bool show_hidden_files = settings->bools.show_hidden_files;
- if (!global || !savestate_auto_index)
- return;
- state_dir[0] = state_base[0] = '\0';
- /* Find the file in the same directory as global->savestate_name
- * with the largest numeral suffix.
- *
- * E.g. /foo/path/content.state, will try to find
- * /foo/path/content.state%d, where %d is the largest number available.
- */
- fill_pathname_basedir(state_dir, global->name.savestate,
- sizeof(state_dir));
- dir_list = dir_list_new_special(state_dir, DIR_LIST_PLAIN, NULL,
- show_hidden_files);
- if (!dir_list)
- return;
- fill_pathname_base(state_base, global->name.savestate,
- sizeof(state_base));
- for (i = 0; i < dir_list->size; i++)
- {
- unsigned idx;
- char elem_base[128] = {0};
- const char *end = NULL;
- const char *dir_elem = dir_list->elems[i].data;
- fill_pathname_base(elem_base, dir_elem, sizeof(elem_base));
- if (strstr(elem_base, state_base) != elem_base)
- continue;
- end = dir_elem + strlen(dir_elem);
- while ((end > dir_elem) && ISDIGIT((int)end[-1]))
- end--;
- idx = (unsigned)strtoul(end, NULL, 0);
- if (idx > max_idx)
- max_idx = idx;
- }
- dir_list_free(dir_list);
- configuration_set_int(settings, settings->ints.state_slot, max_idx);
- RARCH_LOG("%s: #%d\n",
- msg_hash_to_str(MSG_FOUND_LAST_STATE_SLOT),
- max_idx);
- }
- static void command_event_set_savestate_garbage_collect(
- settings_t *settings,
- const global_t *global,
- struct rarch_state *p_rarch)
- {
- size_t i, cnt = 0;
- char state_dir[PATH_MAX_LENGTH];
- char state_base[PATH_MAX_LENGTH];
- struct string_list *dir_list = NULL;
- unsigned min_idx = UINT_MAX;
- const char *oldest_save = NULL;
- unsigned max_to_keep = settings->uints.savestate_max_keep;
- bool show_hidden_files = settings->bools.show_hidden_files;
- if (!global || (max_to_keep == 0))
- return;
- state_dir[0] = '\0';
- state_base[0] = '\0';
- /* Similar to command_event_set_savestate_auto_index(),
- * this will find the lowest numbered save-state */
- fill_pathname_basedir(state_dir, global->name.savestate,
- sizeof(state_dir));
- dir_list = dir_list_new_special(state_dir, DIR_LIST_PLAIN, NULL,
- show_hidden_files);
- if (!dir_list)
- return;
- fill_pathname_base(state_base, global->name.savestate,
- sizeof(state_base));
- for (i = 0; i < dir_list->size; i++)
- {
- unsigned idx;
- char elem_base[128];
- const char *end = NULL;
- const char *dir_elem = dir_list->elems[i].data;
- const char *ext = NULL;
- elem_base[0] = '\0';
- if (string_is_empty(dir_elem))
- continue;
- fill_pathname_base(elem_base, dir_elem, sizeof(elem_base));
- /* Only consider files with a '.state' extension
- * > i.e. Ignore '.state.auto', '.state.bak', etc. */
- ext = path_get_extension(elem_base);
- if (string_is_empty(ext) ||
- !string_starts_with_size(ext, "state", STRLEN_CONST("state")))
- continue;
- /* Check whether this file is associated with
- * the current content */
- if (!string_starts_with(elem_base, state_base))
- continue;
- /* This looks like a valid save */
- cnt++;
- /* > Get index */
- end = dir_elem + strlen(dir_elem);
- while ((end > dir_elem) && ISDIGIT((int)end[-1]))
- end--;
- idx = string_to_unsigned(end);
- /* > Check if this is the lowest index so far */
- if (idx < min_idx)
- {
- min_idx = idx;
- oldest_save = dir_elem;
- }
- }
- /* Only delete one save state per save action
- * > Conservative behaviour, designed to minimise
- * the risk of deleting multiple incorrect files
- * in case of accident */
- if (!string_is_empty(oldest_save) && (cnt > max_to_keep))
- filestream_delete(oldest_save);
- dir_list_free(dir_list);
- }
- static bool event_init_content(
- settings_t *settings,
- struct rarch_state *p_rarch)
- {
- bool contentless = false;
- bool is_inited = false;
- #ifdef HAVE_CHEEVOS
- bool cheevos_enable =
- settings->bools.cheevos_enable;
- bool cheevos_hardcore_mode_enable =
- settings->bools.cheevos_hardcore_mode_enable;
- #endif
- global_t *global = &p_rarch->g_extern;
- const enum rarch_core_type current_core_type = p_rarch->current_core_type;
- content_get_status(&contentless, &is_inited);
- /* TODO/FIXME - just because we have a contentless core does not
- * necessarily mean there should be no SRAM, try to find a solution here */
- p_rarch->rarch_use_sram = (current_core_type == CORE_TYPE_PLAIN)
- && !contentless;
- /* No content to be loaded for dummy core,
- * just successfully exit. */
- if (current_core_type == CORE_TYPE_DUMMY)
- return true;
- content_set_subsystem_info();
- content_get_status(&contentless, &is_inited);
- if (!contentless)
- path_fill_names(p_rarch);
- if (!content_init())
- {
- p_rarch->runloop_core_running = false;
- return false;
- }
- command_event_set_savestate_auto_index(settings, global, p_rarch);
- if (event_load_save_files(p_rarch->rarch_is_sram_load_disabled))
- RARCH_LOG("[SRAM]: %s.\n",
- msg_hash_to_str(MSG_SKIPPING_SRAM_LOAD));
- /*
- Since the operations are asynchronous we can't
- guarantee users will not use auto_load_state to cheat on
- achievements so we forbid auto_load_state from happening
- if cheevos_enable and cheevos_hardcode_mode_enable
- are true.
- */
- #ifdef HAVE_CHEEVOS
- if (!cheevos_enable || !cheevos_hardcore_mode_enable)
- #endif
- command_event_load_auto_state(settings,
- global, p_rarch);
- #ifdef HAVE_BSV_MOVIE
- bsv_movie_deinit(p_rarch);
- if (bsv_movie_init(p_rarch))
- {
- /* Set granularity upon success */
- configuration_set_uint(settings,
- settings->uints.rewind_granularity, 1);
- }
- #endif
- command_event(CMD_EVENT_NETPLAY_INIT, NULL);
- return true;
- }
- static void update_runtime_log(
- struct rarch_state *p_rarch,
- const char *dir_runtime_log,
- const char *dir_playlist,
- bool log_per_core)
- {
- /* Initialise runtime log file */
- runtime_log_t *runtime_log = runtime_log_init(
- p_rarch->runtime_content_path,
- p_rarch->runtime_core_path,
- dir_runtime_log,
- dir_playlist,
- log_per_core);
- if (!runtime_log)
- return;
- /* Add additional runtime */
- runtime_log_add_runtime_usec(runtime_log,
- p_rarch->libretro_core_runtime_usec);
- /* Update 'last played' entry */
- runtime_log_set_last_played_now(runtime_log);
- /* Save runtime log file */
- runtime_log_save(runtime_log);
- /* Clean up */
- free(runtime_log);
- }
- static void command_event_runtime_log_deinit(struct rarch_state *p_rarch)
- {
- char log[PATH_MAX_LENGTH] = {0};
- unsigned hours = 0;
- unsigned minutes = 0;
- unsigned seconds = 0;
- int n = 0;
- runtime_log_convert_usec2hms(
- p_rarch->libretro_core_runtime_usec,
- &hours, &minutes, &seconds);
- n = snprintf(log, sizeof(log),
- "[Core]: Content ran for a total of: %02u hours, %02u minutes, %02u seconds.",
- hours, minutes, seconds);
- if ((n < 0) || (n >= PATH_MAX_LENGTH))
- n = 0; /* Just silence any potential gcc warnings... */
- (void)n;
- RARCH_LOG("%s\n",log);
- /* Only write to file if content has run for a non-zero length of time */
- if (p_rarch->libretro_core_runtime_usec > 0)
- {
- settings_t *settings = p_rarch->configuration_settings;
- bool content_runtime_log = settings->bools.content_runtime_log;
- bool content_runtime_log_aggregate = settings->bools.content_runtime_log_aggregate;
- const char *dir_runtime_log = settings->paths.directory_runtime_log;
- const char *dir_playlist = settings->paths.directory_playlist;
- /* Per core logging */
- if (content_runtime_log)
- update_runtime_log(p_rarch, dir_runtime_log, dir_playlist, true);
- /* Aggregate logging */
- if (content_runtime_log_aggregate)
- update_runtime_log(p_rarch, dir_runtime_log, dir_playlist, false);
- }
- /* Reset runtime + content/core paths, to prevent any
- * possibility of duplicate logging */
- p_rarch->libretro_core_runtime_usec = 0;
- memset(p_rarch->runtime_content_path, 0, sizeof(p_rarch->runtime_content_path));
- memset(p_rarch->runtime_core_path, 0, sizeof(p_rarch->runtime_core_path));
- }
- static void command_event_runtime_log_init(struct rarch_state *p_rarch)
- {
- const char *content_path = path_get(RARCH_PATH_CONTENT);
- const char *core_path = path_get(RARCH_PATH_CORE);
- p_rarch->libretro_core_runtime_last = cpu_features_get_time_usec();
- p_rarch->libretro_core_runtime_usec = 0;
- /* Have to cache content and core path here, otherwise
- * logging fails if new content is loaded without
- * closing existing content
- * i.e. RARCH_PATH_CONTENT and RARCH_PATH_CORE get
- * updated when the new content is loaded, which
- * happens *before* command_event_runtime_log_deinit
- * -> using RARCH_PATH_CONTENT and RARCH_PATH_CORE
- * directly in command_event_runtime_log_deinit
- * can therefore lead to the runtime of the currently
- * loaded content getting written to the *new*
- * content's log file... */
- memset(p_rarch->runtime_content_path,
- 0, sizeof(p_rarch->runtime_content_path));
- memset(p_rarch->runtime_core_path,
- 0, sizeof(p_rarch->runtime_core_path));
- if (!string_is_empty(content_path))
- strlcpy(p_rarch->runtime_content_path,
- content_path,
- sizeof(p_rarch->runtime_content_path));
- if (!string_is_empty(core_path))
- strlcpy(p_rarch->runtime_core_path,
- core_path,
- sizeof(p_rarch->runtime_core_path));
- }
- static void retroarch_set_frame_limit(
- struct rarch_state *p_rarch,
- float fastforward_ratio_orig)
- {
- struct retro_system_av_info *av_info = &p_rarch->video_driver_av_info;
- float fastforward_ratio = (fastforward_ratio_orig == 0.0f)
- ? 1.0f : fastforward_ratio_orig;
- p_rarch->frame_limit_last_time = cpu_features_get_time_usec();
- p_rarch->frame_limit_minimum_time = (retro_time_t)roundf(1000000.0f
- / (av_info->timing.fps * fastforward_ratio));
- }
- static bool command_event_init_core(
- settings_t *settings,
- struct rarch_state *p_rarch,
- enum rarch_core_type type)
- {
- #ifdef HAVE_CONFIGFILE
- bool auto_overrides_enable = settings->bools.auto_overrides_enable;
- bool auto_remaps_enable = settings->bools.auto_remaps_enable;
- const char *dir_input_remapping = settings->paths.directory_input_remapping;
- #endif
- bool show_set_initial_disk_msg = settings->bools.notification_show_set_initial_disk;
- unsigned poll_type_behavior = settings->uints.input_poll_type_behavior;
- float fastforward_ratio = settings->floats.fastforward_ratio;
- rarch_system_info_t *sys_info = &p_rarch->runloop_system;
- if (!init_libretro_symbols(p_rarch,
- type, &p_rarch->current_core))
- return false;
- if (!p_rarch->current_core.retro_run)
- p_rarch->current_core.retro_run = retro_run_null;
- p_rarch->current_core.symbols_inited = true;
- p_rarch->current_core.retro_get_system_info(&sys_info->info);
- if (!sys_info->info.library_name)
- sys_info->info.library_name = msg_hash_to_str(MSG_UNKNOWN);
- if (!sys_info->info.library_version)
- sys_info->info.library_version = "v0";
- fill_pathname_join_concat_noext(
- p_rarch->video_driver_title_buf,
- msg_hash_to_str(MSG_PROGRAM),
- " ",
- sys_info->info.library_name,
- sizeof(p_rarch->video_driver_title_buf));
- strlcat(p_rarch->video_driver_title_buf, " ",
- sizeof(p_rarch->video_driver_title_buf));
- strlcat(p_rarch->video_driver_title_buf,
- sys_info->info.library_version,
- sizeof(p_rarch->video_driver_title_buf));
- strlcpy(sys_info->valid_extensions,
- sys_info->info.valid_extensions ?
- sys_info->info.valid_extensions : DEFAULT_EXT,
- sizeof(sys_info->valid_extensions));
- #ifdef HAVE_CONFIGFILE
- if (auto_overrides_enable)
- p_rarch->runloop_overrides_active =
- config_load_override(&p_rarch->runloop_system);
- #endif
- #ifdef HAVE_CHEEVOS
- /* assume the core supports achievements unless it tells us otherwise */
- rcheevos_set_support_cheevos(true);
- #endif
- /* Load auto-shaders on the next occasion */
- #if defined(HAVE_CG) || defined(HAVE_GLSL) || defined(HAVE_SLANG) || defined(HAVE_HLSL)
- p_rarch->shader_presets_need_reload = true;
- p_rarch->shader_delay_timer.timer_begin = false; /* not initialized */
- p_rarch->shader_delay_timer.timer_end = false; /* not expired */
- #endif
- /* reset video format to libretro's default */
- p_rarch->video_driver_pix_fmt = RETRO_PIXEL_FORMAT_0RGB1555;
- p_rarch->current_core.retro_set_environment(rarch_environment_cb);
- #ifdef HAVE_CONFIGFILE
- if (auto_remaps_enable)
- config_load_remap(dir_input_remapping, &p_rarch->runloop_system);
- #endif
- /* Per-core saves: reset redirection paths */
- path_set_redirect(p_rarch);
- video_driver_set_cached_frame_ptr(NULL);
- p_rarch->current_core.retro_init();
- p_rarch->current_core.inited = true;
- /* Attempt to set initial disk index */
- disk_control_set_initial_index(
- &sys_info->disk_control,
- path_get(RARCH_PATH_CONTENT),
- p_rarch->current_savefile_dir);
- if (!event_init_content(settings, p_rarch))
- return false;
- /* Verify that initial disk index was set correctly */
- disk_control_verify_initial_index(&sys_info->disk_control,
- show_set_initial_disk_msg);
- if (!core_load(p_rarch, poll_type_behavior))
- return false;
- retroarch_set_frame_limit(p_rarch, fastforward_ratio);
- command_event_runtime_log_init(p_rarch);
- return true;
- }
- static bool command_event_save_auto_state(
- settings_t *settings,
- global_t *global,
- struct rarch_state *p_rarch)
- {
- bool ret = false;
- char savestate_name_auto[PATH_MAX_LENGTH];
- bool savestate_auto_save = settings->bools.savestate_auto_save;
- const enum rarch_core_type
- current_core_type = p_rarch->current_core_type;
- if (!global || !savestate_auto_save)
- return false;
- if (current_core_type == CORE_TYPE_DUMMY)
- return false;
- if (string_is_empty(path_basename(path_get(RARCH_PATH_BASENAME))))
- return false;
- #ifdef HAVE_CHEEVOS
- if (rcheevos_hardcore_active())
- return false;
- #endif
- savestate_name_auto[0] = '\0';
- fill_pathname_noext(savestate_name_auto, global->name.savestate,
- ".auto", sizeof(savestate_name_auto));
- ret = content_save_state((const char*)savestate_name_auto, true, true);
- RARCH_LOG("%s \"%s\" %s.\n",
- msg_hash_to_str(MSG_AUTO_SAVE_STATE_TO),
- savestate_name_auto, ret ?
- "succeeded" : "failed");
- return true;
- }
- #ifdef HAVE_CONFIGFILE
- static bool command_event_save_config(
- const char *config_path,
- char *s, size_t len)
- {
- char log[PATH_MAX_LENGTH];
- bool path_exists = !string_is_empty(config_path);
- const char *str = path_exists ? config_path :
- path_get(RARCH_PATH_CONFIG);
- if (path_exists && config_save_file(config_path))
- {
- snprintf(s, len, "%s \"%s\".",
- msg_hash_to_str(MSG_SAVED_NEW_CONFIG_TO),
- config_path);
- strcpy_literal(log, "[Config]: ");
- strlcat(log, s, sizeof(log));
- RARCH_LOG("%s\n", log);
- return true;
- }
- if (!string_is_empty(str))
- {
- snprintf(s, len, "%s \"%s\".",
- msg_hash_to_str(MSG_FAILED_SAVING_CONFIG_TO),
- str);
- strcpy_literal(log, "[Config]: ");
- strlcat(log, s, sizeof(log));
- RARCH_ERR("%s\n", log);
- }
- return false;
- }
- /**
- * command_event_save_core_config:
- *
- * Saves a new (core) configuration to a file. Filename is based
- * on heuristics to avoid typing.
- *
- * Returns: true (1) on success, otherwise false (0).
- **/
- static bool command_event_save_core_config(
- struct rarch_state *p_rarch,
- const char *dir_menu_config)
- {
- char msg[128];
- char config_name[PATH_MAX_LENGTH];
- char config_path[PATH_MAX_LENGTH];
- char config_dir[PATH_MAX_LENGTH];
- bool found_path = false;
- bool overrides_active = false;
- const char *core_path = NULL;
- msg[0] = '\0';
- config_dir[0] = '\0';
- if (!string_is_empty(dir_menu_config))
- strlcpy(config_dir, dir_menu_config, sizeof(config_dir));
- else if (!path_is_empty(RARCH_PATH_CONFIG)) /* Fallback */
- fill_pathname_basedir(config_dir, path_get(RARCH_PATH_CONFIG),
- sizeof(config_dir));
- if (string_is_empty(config_dir))
- {
- runloop_msg_queue_push(msg_hash_to_str(MSG_CONFIG_DIRECTORY_NOT_SET), 1, 180, true, NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO);
- RARCH_ERR("[Config]: %s\n", msg_hash_to_str(MSG_CONFIG_DIRECTORY_NOT_SET));
- return false;
- }
- core_path = path_get(RARCH_PATH_CORE);
- config_name[0] = '\0';
- config_path[0] = '\0';
- /* Infer file name based on libretro core. */
- if (path_is_valid(core_path))
- {
- unsigned i;
- RARCH_LOG("%s\n", msg_hash_to_str(MSG_USING_CORE_NAME_FOR_NEW_CONFIG));
- /* In case of collision, find an alternative name. */
- for (i = 0; i < 16; i++)
- {
- char tmp[64] = {0};
- fill_pathname_base_noext(
- config_name,
- core_path,
- sizeof(config_name));
- fill_pathname_join(config_path, config_dir, config_name,
- sizeof(config_path));
- if (i)
- snprintf(tmp, sizeof(tmp), "-%u", i);
- strlcat(tmp, ".cfg", sizeof(tmp));
- strlcat(config_path, tmp, sizeof(config_path));
- if (!path_is_valid(config_path))
- {
- found_path = true;
- break;
- }
- }
- }
- if (!found_path)
- {
- /* Fallback to system time... */
- RARCH_WARN("[Config]: %s\n",
- msg_hash_to_str(MSG_CANNOT_INFER_NEW_CONFIG_PATH));
- fill_dated_filename(config_name, ".cfg", sizeof(config_name));
- fill_pathname_join(config_path, config_dir, config_name,
- sizeof(config_path));
- }
- if (p_rarch->runloop_overrides_active)
- {
- /* Overrides block config file saving,
- * make it appear as overrides weren't enabled
- * for a manual save. */
- p_rarch->runloop_overrides_active = false;
- overrides_active = true;
- }
- #ifdef HAVE_CONFIGFILE
- command_event_save_config(config_path, msg, sizeof(msg));
- #endif
- if (!string_is_empty(msg))
- runloop_msg_queue_push(msg, 1, 180, true, NULL,
- MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO);
- p_rarch->runloop_overrides_active = overrides_active;
- return true;
- }
- /**
- * event_save_current_config:
- *
- * Saves current configuration file to disk, and (optionally)
- * autosave state.
- **/
- static void command_event_save_current_config(
- struct rarch_state *p_rarch,
- enum override_type type)
- {
- char msg[128];
- msg[0] = '\0';
- switch (type)
- {
- case OVERRIDE_NONE:
- if (path_is_empty(RARCH_PATH_CONFIG))
- strcpy_literal(msg, "[Config]: Config directory not set, cannot save configuration.");
- else
- command_event_save_config(path_get(RARCH_PATH_CONFIG), msg, sizeof(msg));
- break;
- case OVERRIDE_GAME:
- case OVERRIDE_CORE:
- case OVERRIDE_CONTENT_DIR:
- if (config_save_overrides(type, &p_rarch->runloop_system))
- {
- strlcpy(msg, msg_hash_to_str(MSG_OVERRIDES_SAVED_SUCCESSFULLY), sizeof(msg));
- RARCH_LOG("[Config - Overrides]: %s\n", msg);
- /* set overrides to active so the original config can be
- restored after closing content */
- p_rarch->runloop_overrides_active = true;
- }
- else
- {
- strlcpy(msg, msg_hash_to_str(MSG_OVERRIDES_ERROR_SAVING), sizeof(msg));
- RARCH_ERR("[Config - Overrides]: %s\n", msg);
- }
- break;
- }
- if (!string_is_empty(msg))
- runloop_msg_queue_push(msg, 1, 180, true, NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO);
- }
- #endif
- static void command_event_undo_save_state(char *s, size_t len)
- {
- if (content_undo_save_buf_is_empty())
- {
- strlcpy(s,
- msg_hash_to_str(MSG_NO_SAVE_STATE_HAS_BEEN_OVERWRITTEN_YET), len);
- return;
- }
- if (!content_undo_save_state())
- {
- strlcpy(s,
- msg_hash_to_str(MSG_FAILED_TO_UNDO_SAVE_STATE), len);
- return;
- }
- strlcpy(s,
- msg_hash_to_str(MSG_UNDOING_SAVE_STATE), len);
- }
- static void command_event_undo_load_state(char *s, size_t len)
- {
- if (content_undo_load_buf_is_empty())
- {
- strlcpy(s,
- msg_hash_to_str(MSG_NO_STATE_HAS_BEEN_LOADED_YET),
- len);
- return;
- }
- if (!content_undo_load_state())
- {
- snprintf(s, len, "%s \"%s\".",
- msg_hash_to_str(MSG_FAILED_TO_UNDO_LOAD_STATE),
- "RAM");
- return;
- }
- #ifdef HAVE_NETWORKING
- netplay_driver_ctl(RARCH_NETPLAY_CTL_LOAD_SAVESTATE, NULL);
- #endif
- strlcpy(s,
- msg_hash_to_str(MSG_UNDID_LOAD_STATE), len);
- }
- static bool command_event_main_state(
- struct rarch_state *p_rarch,
- unsigned cmd)
- {
- retro_ctx_size_info_t info;
- char msg[128];
- char state_path[16384];
- const global_t *global = &p_rarch->g_extern;
- settings_t *settings = p_rarch->configuration_settings;
- bool ret = false;
- bool push_msg = true;
- state_path[0] = msg[0] = '\0';
- if (global)
- {
- int state_slot = settings->ints.state_slot;
- const char *name_savestate = global->name.savestate;
- if (state_slot > 0)
- snprintf(state_path, sizeof(state_path), "%s%d",
- name_savestate, state_slot);
- else if (state_slot < 0)
- fill_pathname_join_delim(state_path,
- name_savestate, "auto", '.', sizeof(state_path));
- else
- strlcpy(state_path, name_savestate, sizeof(state_path));
- }
- core_serialize_size(&info);
- if (info.size)
- {
- switch (cmd)
- {
- case CMD_EVENT_SAVE_STATE:
- {
- bool savestate_auto_index =
- settings->bools.savestate_auto_index;
- unsigned savestate_max_keep =
- settings->uints.savestate_max_keep;
- bool frame_time_counter_reset_after_save_state =
- settings->bools.frame_time_counter_reset_after_save_state;
- content_save_state(state_path, true, false);
- /* Clean up excess savestates if necessary */
- if (savestate_auto_index && (savestate_max_keep > 0))
- command_event_set_savestate_garbage_collect(settings, global, p_rarch);
- if (frame_time_counter_reset_after_save_state)
- p_rarch->video_driver_frame_time_count = 0;
- ret = true;
- push_msg = false;
- }
- break;
- case CMD_EVENT_LOAD_STATE:
- if (content_load_state(state_path, false, false))
- {
- #ifdef HAVE_CHEEVOS
- if (rcheevos_hardcore_active())
- {
- rcheevos_pause_hardcore();
- runloop_msg_queue_push(msg_hash_to_str(MSG_CHEEVOS_HARDCORE_MODE_DISABLED), 0, 180, true, NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO);
- }
- #endif
- ret = true;
- #ifdef HAVE_NETWORKING
- netplay_driver_ctl(RARCH_NETPLAY_CTL_LOAD_SAVESTATE, NULL);
- #endif
- {
- bool frame_time_counter_reset_after_load_state =
- settings->bools.frame_time_counter_reset_after_load_state;
- if (frame_time_counter_reset_after_load_state)
- p_rarch->video_driver_frame_time_count = 0;
- }
- }
- push_msg = false;
- break;
- case CMD_EVENT_UNDO_LOAD_STATE:
- command_event_undo_load_state(msg, sizeof(msg));
- ret = true;
- break;
- case CMD_EVENT_UNDO_SAVE_STATE:
- command_event_undo_save_state(msg, sizeof(msg));
- ret = true;
- break;
- }
- }
- else
- strlcpy(msg, msg_hash_to_str(
- MSG_CORE_DOES_NOT_SUPPORT_SAVESTATES), sizeof(msg));
- if (push_msg)
- runloop_msg_queue_push(msg, 2, 180, true, NULL,
- MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO);
- if (!string_is_empty(msg))
- RARCH_LOG("%s\n", msg);
- return ret;
- }
- static bool command_event_resize_windowed_scale(struct rarch_state *p_rarch)
- {
- unsigned idx = 0;
- settings_t *settings = p_rarch->configuration_settings;
- unsigned window_scale = p_rarch->runloop_pending_windowed_scale;
- bool video_fullscreen = settings->bools.video_fullscreen;
- if (window_scale == 0)
- return false;
- configuration_set_float(settings, settings->floats.video_scale, (float)window_scale);
- if (!video_fullscreen)
- command_event(CMD_EVENT_REINIT, NULL);
- rarch_ctl(RARCH_CTL_SET_WINDOWED_SCALE, &idx);
- return true;
- }
- static bool input_driver_grab_mouse(struct rarch_state *p_rarch)
- {
- if (!p_rarch->current_input || !p_rarch->current_input->grab_mouse)
- return false;
- p_rarch->current_input->grab_mouse(p_rarch->current_input_data, true);
- p_rarch->input_driver_grab_mouse_state = true;
- return true;
- }
- static bool input_driver_ungrab_mouse(struct rarch_state *p_rarch)
- {
- if (!p_rarch->current_input || !p_rarch->current_input->grab_mouse)
- return false;
- p_rarch->current_input->grab_mouse(p_rarch->current_input_data, false);
- p_rarch->input_driver_grab_mouse_state = false;
- return true;
- }
- static void command_event_reinit(struct rarch_state *p_rarch,
- const int flags)
- {
- settings_t *settings = p_rarch->configuration_settings;
- #ifdef HAVE_MENU
- bool video_fullscreen = settings->bools.video_fullscreen;
- bool adaptive_vsync = settings->bools.video_adaptive_vsync;
- unsigned swap_interval = settings->uints.video_swap_interval;
- #endif
- enum input_game_focus_cmd_type game_focus_cmd = GAME_FOCUS_CMD_REAPPLY;
- video_driver_reinit(flags);
- /* Poll input to avoid possibly stale data to corrupt things. */
- if ( p_rarch->joypad &&
- p_rarch->joypad->poll)
- p_rarch->joypad->poll();
- #ifdef HAVE_MFI
- if ( p_rarch->sec_joypad &&
- p_rarch->sec_joypad->poll)
- p_rarch->sec_joypad->poll();
- #endif
- if ( p_rarch->current_input &&
- p_rarch->current_input->poll)
- p_rarch->current_input->poll(p_rarch->current_input_data);
- command_event(CMD_EVENT_GAME_FOCUS_TOGGLE, &game_focus_cmd);
- #ifdef HAVE_MENU
- p_rarch->dispgfx.framebuf_dirty = true;
- if (video_fullscreen)
- video_driver_hide_mouse();
- if (p_rarch->menu_driver_alive && p_rarch->current_video->set_nonblock_state)
- p_rarch->current_video->set_nonblock_state(
- p_rarch->video_driver_data, false,
- video_driver_test_all_flags(GFX_CTX_FLAGS_ADAPTIVE_VSYNC) &&
- adaptive_vsync,
- swap_interval);
- #endif
- }
- static void retroarch_pause_checks(struct rarch_state *p_rarch)
- {
- #ifdef HAVE_DISCORD
- discord_userdata_t userdata;
- #endif
- bool is_paused = p_rarch->runloop_paused;
- bool is_idle = p_rarch->runloop_idle;
- #if defined(HAVE_GFX_WIDGETS)
- bool widgets_active = p_rarch->widgets_active;
- if (widgets_active)
- p_rarch->gfx_widgets_paused = is_paused;
- #endif
- if (is_paused)
- {
- RARCH_LOG("[Core]: %s\n", msg_hash_to_str(MSG_PAUSED));
- #if defined(HAVE_GFX_WIDGETS)
- if (!widgets_active)
- #endif
- runloop_msg_queue_push(msg_hash_to_str(MSG_PAUSED), 1,
- 1, true,
- NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO);
- if (!is_idle)
- video_driver_cached_frame();
- #ifdef HAVE_DISCORD
- userdata.status = DISCORD_PRESENCE_GAME_PAUSED;
- command_event(CMD_EVENT_DISCORD_UPDATE, &userdata);
- #endif
- }
- else
- {
- RARCH_LOG("[Core]: %s\n", msg_hash_to_str(MSG_UNPAUSED));
- }
- #if defined(HAVE_TRANSLATE) && defined(HAVE_GFX_WIDGETS)
- if (p_rarch->dispwidget_st.ai_service_overlay_state == 1)
- gfx_widgets_ai_service_overlay_unload(&p_rarch->dispwidget_st);
- #endif
- }
- static void retroarch_frame_time_free(struct rarch_state *p_rarch)
- {
- memset(&p_rarch->runloop_frame_time, 0,
- sizeof(struct retro_frame_time_callback));
- p_rarch->runloop_frame_time_last = 0;
- p_rarch->runloop_max_frames = 0;
- }
- static void retroarch_audio_buffer_status_free(struct rarch_state *p_rarch)
- {
- memset(&p_rarch->runloop_audio_buffer_status, 0,
- sizeof(struct retro_audio_buffer_status_callback));
- p_rarch->runloop_audio_latency = 0;
- }
- static void retroarch_game_focus_free(struct rarch_state *p_rarch)
- {
- /* Ensure that game focus mode is disabled */
- if (p_rarch->game_focus_state.enabled)
- {
- enum input_game_focus_cmd_type game_focus_cmd = GAME_FOCUS_CMD_OFF;
- command_event(CMD_EVENT_GAME_FOCUS_TOGGLE, &game_focus_cmd);
- }
- p_rarch->game_focus_state.enabled = false;
- p_rarch->game_focus_state.core_requested = false;
- }
- static void retroarch_system_info_free(struct rarch_state *p_rarch)
- {
- rarch_system_info_t *sys_info = &p_rarch->runloop_system;
- if (sys_info->subsystem.data)
- free(sys_info->subsystem.data);
- sys_info->subsystem.data = NULL;
- sys_info->subsystem.size = 0;
- if (sys_info->ports.data)
- free(sys_info->ports.data);
- sys_info->ports.data = NULL;
- sys_info->ports.size = 0;
- if (sys_info->mmaps.descriptors)
- free((void *)sys_info->mmaps.descriptors);
- sys_info->mmaps.descriptors = NULL;
- sys_info->mmaps.num_descriptors = 0;
- p_rarch->runloop_key_event = NULL;
- p_rarch->runloop_frontend_key_event = NULL;
- p_rarch->audio_callback.callback = NULL;
- p_rarch->audio_callback.set_state = NULL;
- sys_info->info.library_name = NULL;
- sys_info->info.library_version = NULL;
- sys_info->info.valid_extensions = NULL;
- sys_info->info.need_fullpath = false;
- sys_info->info.block_extract = false;
- memset(&p_rarch->runloop_system, 0, sizeof(rarch_system_info_t));
- }
- static bool libretro_get_system_info(
- struct rarch_state *p_rarch,
- const char *path,
- struct retro_system_info *info,
- bool *load_no_content);
- #ifdef HAVE_RUNAHEAD
- static void runahead_clear_variables(struct rarch_state *p_rarch)
- {
- p_rarch->runahead_save_state_size = 0;
- p_rarch->runahead_save_state_size_known = false;
- p_rarch->runahead_video_driver_is_active = true;
- p_rarch->runahead_available = true;
- p_rarch->runahead_secondary_core_available = true;
- p_rarch->runahead_force_input_dirty = true;
- p_rarch->runahead_last_frame_count = 0;
- }
- #endif
- /**
- * command_event:
- * @cmd : Event command index.
- *
- * Performs program event command with index @cmd.
- *
- * Returns: true (1) on success, otherwise false (0).
- **/
- bool command_event(enum event_command cmd, void *data)
- {
- bool boolean = false;
- struct rarch_state *p_rarch = &rarch_st;
- settings_t *settings = p_rarch->configuration_settings;
- switch (cmd)
- {
- case CMD_EVENT_RELOAD_CONFIG:
- config_load(&p_rarch->g_extern);
- break;
- case CMD_EVENT_SAVE_FILES:
- event_save_files(p_rarch->rarch_use_sram);
- break;
- case CMD_EVENT_OVERLAY_DEINIT:
- #ifdef HAVE_OVERLAY
- retroarch_overlay_deinit(p_rarch);
- #endif
- #if defined(HAVE_TRANSLATE) && defined(HAVE_GFX_WIDGETS)
- /* Because the overlay is a display widget,
- * it's going to be written
- * over the menu, so we unset it here. */
- if (p_rarch->dispwidget_st.ai_service_overlay_state != 0)
- gfx_widgets_ai_service_overlay_unload(&p_rarch->dispwidget_st);
- #endif
- break;
- case CMD_EVENT_OVERLAY_INIT:
- #ifdef HAVE_OVERLAY
- retroarch_overlay_init(p_rarch);
- #endif
- break;
- case CMD_EVENT_CHEAT_INDEX_PLUS:
- #ifdef HAVE_CHEATS
- cheat_manager_index_next();
- #endif
- break;
- case CMD_EVENT_CHEAT_INDEX_MINUS:
- #ifdef HAVE_CHEATS
- cheat_manager_index_prev();
- #endif
- break;
- case CMD_EVENT_CHEAT_TOGGLE:
- #ifdef HAVE_CHEATS
- cheat_manager_toggle();
- #endif
- break;
- case CMD_EVENT_SHADER_NEXT:
- #if defined(HAVE_CG) || defined(HAVE_GLSL) || defined(HAVE_SLANG) || defined(HAVE_HLSL)
- dir_check_shader(p_rarch, true, false);
- #endif
- break;
- case CMD_EVENT_SHADER_PREV:
- #if defined(HAVE_CG) || defined(HAVE_GLSL) || defined(HAVE_SLANG) || defined(HAVE_HLSL)
- dir_check_shader(p_rarch, false, true);
- #endif
- break;
- case CMD_EVENT_BSV_RECORDING_TOGGLE:
- #ifdef HAVE_BSV_MOVIE
- if (!recording_is_enabled())
- command_event(CMD_EVENT_RECORD_INIT, NULL);
- else
- command_event(CMD_EVENT_RECORD_DEINIT, NULL);
- bsv_movie_check(p_rarch);
- #endif
- break;
- case CMD_EVENT_AI_SERVICE_TOGGLE:
- {
- #ifdef HAVE_TRANSLATE
- bool ai_service_pause = settings->bools.ai_service_pause;
- if (!settings->bools.ai_service_enable)
- break;
- if (ai_service_pause)
- {
- /* pause on call, unpause on second press. */
- if (!p_rarch->runloop_paused)
- {
- command_event(CMD_EVENT_PAUSE, NULL);
- command_event(CMD_EVENT_AI_SERVICE_CALL, NULL);
- }
- else
- {
- #ifdef HAVE_ACCESSIBILITY
- if (is_accessibility_enabled(p_rarch))
- accessibility_speak_priority(p_rarch,
- (char*) msg_hash_to_str(MSG_UNPAUSED), 10);
- #endif
- command_event(CMD_EVENT_UNPAUSE, NULL);
- }
- }
- else
- {
- /* Don't pause - useful for Text-To-Speech since
- * the audio can't currently play while paused.
- * Also useful for cases when users don't want the
- * core's sound to stop while translating.
- *
- * Also, this mode is required for "auto" translation
- * packages, since you don't want to pause for that.
- */
- if (p_rarch->ai_service_auto == 2)
- {
- /* Auto mode was turned on, but we pressed the
- * toggle button, so turn it off now. */
- p_rarch->ai_service_auto = 0;
- #ifdef HAVE_MENU_WIDGETS
- gfx_widgets_ai_service_overlay_unload(&p_rarch->dispwidget_st);
- #endif
- }
- else
- command_event(CMD_EVENT_AI_SERVICE_CALL, NULL);
- }
- #endif
- break;
- }
- case CMD_EVENT_STREAMING_TOGGLE:
- if (streaming_is_enabled())
- command_event(CMD_EVENT_RECORD_DEINIT, NULL);
- else
- {
- streaming_set_state(true);
- command_event(CMD_EVENT_RECORD_INIT, NULL);
- }
- break;
- case CMD_EVENT_RUNAHEAD_TOGGLE:
- {
- char msg[256];
- msg[0] = '\0';
- settings->bools.run_ahead_enabled =
- !(settings->bools.run_ahead_enabled);
- if (!settings->bools.run_ahead_enabled)
- {
- runloop_msg_queue_push(msg_hash_to_str(MSG_RUNAHEAD_DISABLED),
- 1, 100, false,
- NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO);
- }
- else if (!settings->bools.run_ahead_secondary_instance)
- {
- snprintf(msg, sizeof(msg), msg_hash_to_str(MSG_RUNAHEAD_ENABLED),
- settings->uints.run_ahead_frames);
- runloop_msg_queue_push(
- msg, 1, 100, false,
- NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO);
- }
- else
- {
- snprintf(msg, sizeof(msg), msg_hash_to_str(MSG_RUNAHEAD_ENABLED_WITH_SECOND_INSTANCE),
- settings->uints.run_ahead_frames);
- runloop_msg_queue_push(
- msg, 1, 100, false,
- NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO);
- }
- }
- break;
- case CMD_EVENT_RECORDING_TOGGLE:
- if (recording_is_enabled())
- command_event(CMD_EVENT_RECORD_DEINIT, NULL);
- else
- command_event(CMD_EVENT_RECORD_INIT, NULL);
- break;
- case CMD_EVENT_OSK_TOGGLE:
- if (p_rarch->input_driver_keyboard_linefeed_enable)
- p_rarch->input_driver_keyboard_linefeed_enable = false;
- else
- p_rarch->input_driver_keyboard_linefeed_enable = true;
- break;
- case CMD_EVENT_SET_PER_GAME_RESOLUTION:
- #if defined(GEKKO)
- {
- unsigned width = 0, height = 0;
- command_event(CMD_EVENT_VIDEO_SET_ASPECT_RATIO, NULL);
- if (video_driver_get_video_output_size(&width, &height))
- {
- char msg[128] = {0};
- video_driver_set_video_mode(width, height, true);
- if (width == 0 || height == 0)
- snprintf(msg, sizeof(msg), "%s: DEFAULT",
- msg_hash_to_str(MENU_ENUM_LABEL_VALUE_SCREEN_RESOLUTION));
- else
- snprintf(msg, sizeof(msg),"%s: %dx%d",
- msg_hash_to_str(MENU_ENUM_LABEL_VALUE_SCREEN_RESOLUTION),
- width, height);
- runloop_msg_queue_push(msg, 1, 100, true, NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO);
- }
- }
- #endif
- break;
- case CMD_EVENT_LOAD_CORE_PERSIST:
- {
- core_info_ctx_find_t info_find;
- rarch_system_info_t *system_info = &p_rarch->runloop_system;
- struct retro_system_info *system = &system_info->info;
- const char *core_path = path_get(RARCH_PATH_CORE);
- #if defined(HAVE_DYNAMIC)
- if (string_is_empty(core_path))
- return false;
- #endif
- if (!libretro_get_system_info(
- p_rarch,
- core_path,
- system,
- &system_info->load_no_content))
- return false;
- info_find.path = core_path;
- if (!core_info_load(&info_find, &p_rarch->core_info_st))
- {
- #ifdef HAVE_DYNAMIC
- return false;
- #endif
- }
- }
- break;
- case CMD_EVENT_LOAD_CORE:
- {
- bool success = false;
- subsystem_current_count = 0;
- content_clear_subsystem();
- success = command_event(CMD_EVENT_LOAD_CORE_PERSIST, NULL);
- (void)success;
- #ifndef HAVE_DYNAMIC
- command_event(CMD_EVENT_QUIT, NULL);
- #else
- if (!success)
- return false;
- #endif
- break;
- }
- case CMD_EVENT_LOAD_STATE:
- #ifdef HAVE_BSV_MOVIE
- /* Immutable - disallow savestate load when
- * we absolutely cannot change game state. */
- if (p_rarch->bsv_movie_state_handle)
- return false;
- #endif
- #ifdef HAVE_CHEEVOS
- if (rcheevos_hardcore_active())
- return false;
- #endif
- if (!command_event_main_state(p_rarch, cmd))
- return false;
- break;
- case CMD_EVENT_UNDO_LOAD_STATE:
- if (!command_event_main_state(p_rarch, cmd))
- return false;
- break;
- case CMD_EVENT_UNDO_SAVE_STATE:
- if (!command_event_main_state(p_rarch, cmd))
- return false;
- break;
- case CMD_EVENT_RESIZE_WINDOWED_SCALE:
- if (!command_event_resize_windowed_scale(p_rarch))
- return false;
- break;
- case CMD_EVENT_MENU_TOGGLE:
- #ifdef HAVE_MENU
- if (p_rarch->menu_driver_alive)
- retroarch_menu_running_finished(false);
- else
- retroarch_menu_running();
- #endif
- break;
- case CMD_EVENT_RESET:
- RARCH_LOG("[Core]: %s.\n", msg_hash_to_str(MSG_RESET));
- runloop_msg_queue_push(msg_hash_to_str(MSG_RESET), 1, 120, true, NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO);
- core_reset();
- #ifdef HAVE_CHEEVOS
- #ifdef HAVE_GFX_WIDGETS
- rcheevos_reset_game(p_rarch->widgets_active);
- #else
- rcheevos_reset_game(false);
- #endif
- #endif
- #if HAVE_NETWORKING
- netplay_driver_ctl(RARCH_NETPLAY_CTL_RESET, NULL);
- #endif
- return false;
- case CMD_EVENT_SAVE_STATE:
- {
- bool savestate_auto_index = settings->bools.savestate_auto_index;
- int state_slot = settings->ints.state_slot;
- if (savestate_auto_index)
- {
- int new_state_slot = state_slot + 1;
- configuration_set_int(settings, settings->ints.state_slot, new_state_slot);
- }
- }
- if (!command_event_main_state(p_rarch, cmd))
- return false;
- break;
- case CMD_EVENT_SAVE_STATE_DECREMENT:
- {
- int state_slot = settings->ints.state_slot;
- /* Slot -1 is (auto) slot. */
- if (state_slot >= 0)
- {
- int new_state_slot = state_slot - 1;
- configuration_set_int(settings, settings->ints.state_slot, new_state_slot);
- }
- }
- break;
- case CMD_EVENT_SAVE_STATE_INCREMENT:
- {
- int new_state_slot = settings->ints.state_slot + 1;
- configuration_set_int(settings, settings->ints.state_slot, new_state_slot);
- }
- break;
- case CMD_EVENT_TAKE_SCREENSHOT:
- #ifdef HAVE_SCREENSHOTS
- {
- const char *dir_screenshot = settings->paths.directory_screenshot;
- if (!take_screenshot(dir_screenshot,
- path_get(RARCH_PATH_BASENAME), false,
- video_driver_cached_frame_has_valid_framebuffer(), false, true))
- return false;
- }
- #endif
- break;
- case CMD_EVENT_UNLOAD_CORE:
- {
- bool contentless = false;
- bool is_inited = false;
- content_ctx_info_t content_info = {0};
- global_t *global = &p_rarch->g_extern;
- rarch_system_info_t *sys_info = &p_rarch->runloop_system;
- content_get_status(&contentless, &is_inited);
- p_rarch->runloop_core_running = false;
- /* Save last selected disk index, if required */
- if (sys_info)
- disk_control_save_image_index(&sys_info->disk_control);
- command_event_runtime_log_deinit(p_rarch);
- command_event_save_auto_state(settings,
- global, p_rarch);
- #ifdef HAVE_CONFIGFILE
- if (p_rarch->runloop_overrides_active)
- {
- command_event_disable_overrides(p_rarch);
- if (!settings->bools.video_fullscreen)
- {
- video_driver_show_mouse();
- input_driver_ungrab_mouse(p_rarch);
- }
- }
- #endif
- #if defined(HAVE_CG) || defined(HAVE_GLSL) || defined(HAVE_SLANG) || defined(HAVE_HLSL)
- p_rarch->runtime_shader_preset[0] = '\0';
- #endif
- video_driver_restore_cached(p_rarch, settings);
- #ifdef HAVE_CONFIGFILE
- if ( p_rarch->runloop_remaps_core_active
- || p_rarch->runloop_remaps_content_dir_active
- || p_rarch->runloop_remaps_game_active
- )
- input_remapping_set_defaults(true);
- #endif
- if (is_inited)
- {
- #ifdef HAVE_MENU
- if ( (settings->uints.quit_on_close_content == QUIT_ON_CLOSE_CONTENT_CLI && global->launched_from_cli)
- || settings->uints.quit_on_close_content == QUIT_ON_CLOSE_CONTENT_ENABLED
- )
- command_event(CMD_EVENT_QUIT, NULL);
- #endif
- if (!task_push_start_dummy_core(&content_info))
- return false;
- }
- #ifdef HAVE_DISCORD
- if (discord_is_inited)
- {
- discord_userdata_t userdata;
- userdata.status = DISCORD_PRESENCE_NETPLAY_NETPLAY_STOPPED;
- command_event(CMD_EVENT_DISCORD_UPDATE, &userdata);
- userdata.status = DISCORD_PRESENCE_MENU;
- command_event(CMD_EVENT_DISCORD_UPDATE, &userdata);
- }
- #endif
- #ifdef HAVE_DYNAMIC
- path_clear(RARCH_PATH_CORE);
- retroarch_system_info_free(p_rarch);
- #endif
- if (is_inited)
- {
- subsystem_current_count = 0;
- content_clear_subsystem();
- }
- }
- break;
- case CMD_EVENT_CLOSE_CONTENT:
- #ifdef HAVE_MENU
- /* Closing content via hotkey requires toggling menu
- * and resetting the position later on to prevent
- * going to empty Quick Menu */
- if (!p_rarch->menu_driver_alive)
- {
- p_rarch->menu_driver_state.pending_close_content = true;
- command_event(CMD_EVENT_MENU_TOGGLE, NULL);
- }
- #else
- command_event(CMD_EVENT_QUIT, NULL);
- #endif
- break;
- case CMD_EVENT_QUIT:
- if (!retroarch_main_quit())
- return false;
- break;
- case CMD_EVENT_CHEEVOS_HARDCORE_MODE_TOGGLE:
- #ifdef HAVE_CHEEVOS
- rcheevos_toggle_hardcore_paused();
- #endif
- break;
- case CMD_EVENT_REINIT_FROM_TOGGLE:
- p_rarch->rarch_force_fullscreen = false;
- /* this fallthrough is on purpose, it should do
- a CMD_EVENT_REINIT too */
- case CMD_EVENT_REINIT:
- command_event_reinit(p_rarch,
- data ? *(const int*)data : DRIVERS_CMD_ALL);
- break;
- case CMD_EVENT_CHEATS_APPLY:
- #ifdef HAVE_CHEATS
- cheat_manager_apply_cheats();
- #endif
- break;
- case CMD_EVENT_REWIND_DEINIT:
- #ifdef HAVE_REWIND
- state_manager_event_deinit(&p_rarch->rewind_st);
- #endif
- break;
- case CMD_EVENT_REWIND_INIT:
- #ifdef HAVE_REWIND
- {
- bool rewind_enable = settings->bools.rewind_enable;
- size_t rewind_buf_size = settings->sizes.rewind_buffer_size;
- #ifdef HAVE_CHEEVOS
- if (rcheevos_hardcore_active())
- return false;
- #endif
- if (rewind_enable)
- {
- #ifdef HAVE_NETWORKING
- /* Only enable state manager if netplay is not underway
- TODO/FIXME: Add a setting for these tweaks */
- if (!netplay_driver_ctl(
- RARCH_NETPLAY_CTL_IS_ENABLED, NULL))
- #endif
- {
- state_manager_event_init(&p_rarch->rewind_st,
- (unsigned)rewind_buf_size);
- }
- }
- }
- #endif
- break;
- case CMD_EVENT_REWIND_TOGGLE:
- #ifdef HAVE_REWIND
- {
- bool rewind_enable = settings->bools.rewind_enable;
- if (rewind_enable)
- command_event(CMD_EVENT_REWIND_INIT, NULL);
- else
- command_event(CMD_EVENT_REWIND_DEINIT, NULL);
- }
- #endif
- break;
- case CMD_EVENT_AUTOSAVE_INIT:
- #ifdef HAVE_THREADS
- retroarch_autosave_deinit(p_rarch);
- {
- #ifdef HAVE_NETWORKING
- unsigned autosave_interval =
- settings->uints.autosave_interval;
- /* Only enable state manager if netplay is not underway
- TODO/FIXME: Add a setting for these tweaks */
- if ( (autosave_interval != 0)
- && !netplay_driver_ctl(
- RARCH_NETPLAY_CTL_IS_ENABLED, NULL))
- #endif
- p_rarch->runloop_autosave = autosave_init();
- }
- #endif
- break;
- case CMD_EVENT_AUDIO_STOP:
- midi_driver_set_all_sounds_off(p_rarch);
- if (!audio_driver_stop(p_rarch))
- return false;
- break;
- case CMD_EVENT_AUDIO_START:
- if (!audio_driver_start(p_rarch,
- p_rarch->runloop_shutdown_initiated))
- return false;
- break;
- case CMD_EVENT_AUDIO_MUTE_TOGGLE:
- {
- bool audio_mute_enable =
- *(audio_get_bool_ptr(AUDIO_ACTION_MUTE_ENABLE));
- const char *msg = !audio_mute_enable ?
- msg_hash_to_str(MSG_AUDIO_MUTED):
- msg_hash_to_str(MSG_AUDIO_UNMUTED);
- p_rarch->audio_driver_mute_enable =
- !p_rarch->audio_driver_mute_enable;
- #if defined(HAVE_GFX_WIDGETS)
- if (p_rarch->widgets_active)
- gfx_widget_volume_update_and_show(
- settings->floats.audio_volume,
- p_rarch->audio_driver_mute_enable);
- else
- #endif
- runloop_msg_queue_push(msg, 1, 180, true, NULL,
- MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO);
- }
- break;
- case CMD_EVENT_SEND_DEBUG_INFO:
- break;
- case CMD_EVENT_FPS_TOGGLE:
- settings->bools.video_fps_show = !(settings->bools.video_fps_show);
- break;
- case CMD_EVENT_OVERLAY_NEXT:
- /* Switch to the next available overlay screen. */
- #ifdef HAVE_OVERLAY
- {
- bool *check_rotation = (bool*)data;
- bool inp_overlay_auto_rotate = settings->bools.input_overlay_auto_rotate;
- float input_overlay_opacity = settings->floats.input_overlay_opacity;
- if (!p_rarch->overlay_ptr)
- return false;
- p_rarch->overlay_ptr->index = p_rarch->overlay_ptr->next_index;
- p_rarch->overlay_ptr->active = &p_rarch->overlay_ptr->overlays[
- p_rarch->overlay_ptr->index];
- input_overlay_load_active(p_rarch,
- p_rarch->overlay_ptr, input_overlay_opacity);
- p_rarch->overlay_ptr->blocked = true;
- p_rarch->overlay_ptr->next_index = (unsigned)((p_rarch->overlay_ptr->index + 1) % p_rarch->overlay_ptr->size);
- /* Check orientation, if required */
- if (inp_overlay_auto_rotate)
- if (check_rotation)
- if (*check_rotation)
- input_overlay_auto_rotate_(p_rarch,
- p_rarch->overlay_ptr);
- }
- #endif
- break;
- case CMD_EVENT_DSP_FILTER_INIT:
- #ifdef HAVE_DSP_FILTER
- {
- const char *path_audio_dsp_plugin = settings->paths.path_audio_dsp_plugin;
- audio_driver_dsp_filter_free();
- if (string_is_empty(path_audio_dsp_plugin))
- break;
- if (!audio_driver_dsp_filter_init(path_audio_dsp_plugin))
- {
- RARCH_ERR("[DSP]: Failed to initialize DSP filter \"%s\".\n",
- path_audio_dsp_plugin);
- }
- }
- #endif
- break;
- case CMD_EVENT_RECORD_DEINIT:
- p_rarch->recording_enable = false;
- streaming_set_state(false);
- if (!recording_deinit(p_rarch))
- return false;
- break;
- case CMD_EVENT_RECORD_INIT:
- p_rarch->recording_enable = true;
- if (!recording_init(settings, p_rarch))
- {
- command_event(CMD_EVENT_RECORD_DEINIT, NULL);
- return false;
- }
- break;
- case CMD_EVENT_HISTORY_DEINIT:
- if (g_defaults.content_history)
- {
- playlist_write_file(g_defaults.content_history);
- playlist_free(g_defaults.content_history);
- }
- g_defaults.content_history = NULL;
- if (g_defaults.music_history)
- {
- playlist_write_file(g_defaults.music_history);
- playlist_free(g_defaults.music_history);
- }
- g_defaults.music_history = NULL;
- #if defined(HAVE_FFMPEG) || defined(HAVE_MPV)
- if (g_defaults.video_history)
- {
- playlist_write_file(g_defaults.video_history);
- playlist_free(g_defaults.video_history);
- }
- g_defaults.video_history = NULL;
- #endif
- #ifdef HAVE_IMAGEVIEWER
- if (g_defaults.image_history)
- {
- playlist_write_file(g_defaults.image_history);
- playlist_free(g_defaults.image_history);
- }
- g_defaults.image_history = NULL;
- #endif
- break;
- case CMD_EVENT_HISTORY_INIT:
- {
- playlist_config_t playlist_config;
- bool history_list_enable = settings->bools.history_list_enable;
- const char *path_content_history = settings->paths.path_content_history;
- const char *path_content_music_history = settings->paths.path_content_music_history;
- #if defined(HAVE_FFMPEG) || defined(HAVE_MPV)
- const char *path_content_video_history = settings->paths.path_content_video_history;
- #endif
- #ifdef HAVE_IMAGEVIEWER
- const char *path_content_image_history = settings->paths.path_content_image_history;
- #endif
- playlist_config.capacity = settings->uints.content_history_size;
- playlist_config.old_format = settings->bools.playlist_use_old_format;
- playlist_config.compress = settings->bools.playlist_compression;
- playlist_config.fuzzy_archive_match = settings->bools.playlist_fuzzy_archive_match;
- /* don't use relative paths for content, music, video, and image histories */
- playlist_config_set_base_content_directory(&playlist_config, NULL);
- command_event(CMD_EVENT_HISTORY_DEINIT, NULL);
- if (!history_list_enable)
- return false;
- /* Note: Sorting is disabled by default for
- * all content history playlists */
- RARCH_LOG("[Playlist]: %s: [%s].\n",
- msg_hash_to_str(MSG_LOADING_HISTORY_FILE),
- path_content_history);
- playlist_config_set_path(&playlist_config, path_content_history);
- g_defaults.content_history = playlist_init(&playlist_config);
- playlist_set_sort_mode(
- g_defaults.content_history, PLAYLIST_SORT_MODE_OFF);
- RARCH_LOG("[Playlist]: %s: [%s].\n",
- msg_hash_to_str(MSG_LOADING_HISTORY_FILE),
- path_content_music_history);
- playlist_config_set_path(&playlist_config, path_content_music_history);
- g_defaults.music_history = playlist_init(&playlist_config);
- playlist_set_sort_mode(
- g_defaults.music_history, PLAYLIST_SORT_MODE_OFF);
- #if defined(HAVE_FFMPEG) || defined(HAVE_MPV)
- RARCH_LOG("[Playlist]: %s: [%s].\n",
- msg_hash_to_str(MSG_LOADING_HISTORY_FILE),
- path_content_video_history);
- playlist_config_set_path(&playlist_config, path_content_video_history);
- g_defaults.video_history = playlist_init(&playlist_config);
- playlist_set_sort_mode(
- g_defaults.video_history, PLAYLIST_SORT_MODE_OFF);
- #endif
- #ifdef HAVE_IMAGEVIEWER
- RARCH_LOG("[Playlist]: %s: [%s].\n",
- msg_hash_to_str(MSG_LOADING_HISTORY_FILE),
- path_content_image_history);
- playlist_config_set_path(&playlist_config, path_content_image_history);
- g_defaults.image_history = playlist_init(&playlist_config);
- playlist_set_sort_mode(
- g_defaults.image_history, PLAYLIST_SORT_MODE_OFF);
- #endif
- }
- break;
- case CMD_EVENT_CORE_INFO_DEINIT:
- core_info_deinit_list();
- core_info_free_current_core(&p_rarch->core_info_st);
- break;
- case CMD_EVENT_CORE_INFO_INIT:
- {
- char ext_name[255];
- const char *dir_libretro = settings->paths.directory_libretro;
- const char *path_libretro_info = settings->paths.path_libretro_info;
- bool show_hidden_files = settings->bools.show_hidden_files;
- ext_name[0] = '\0';
- command_event(CMD_EVENT_CORE_INFO_DEINIT, NULL);
- if (!frontend_driver_get_core_extension(ext_name, sizeof(ext_name)))
- return false;
- if (!string_is_empty(dir_libretro))
- core_info_init_list(path_libretro_info,
- dir_libretro,
- ext_name,
- show_hidden_files
- );
- }
- break;
- case CMD_EVENT_CORE_DEINIT:
- {
- struct retro_hw_render_callback *hwr = NULL;
- rarch_system_info_t *sys_info = &p_rarch->runloop_system;
- /* Save last selected disk index, if required */
- if (sys_info)
- disk_control_save_image_index(&sys_info->disk_control);
- command_event_runtime_log_deinit(p_rarch);
- content_reset_savestate_backups();
- hwr = VIDEO_DRIVER_GET_HW_CONTEXT_INTERNAL(p_rarch);
- #ifdef HAVE_CHEEVOS
- rcheevos_unload();
- #endif
- command_event_deinit_core(p_rarch, true);
- #ifdef HAVE_RUNAHEAD
- /* If 'runahead_available' is false, then
- * runahead is enabled by the user but an
- * error occurred while the core was running
- * (typically a save state issue). In this
- * case we have to 'manually' reset the runahead
- * runtime variables, otherwise runahead will
- * remain disabled until the user restarts
- * RetroArch */
- if (!p_rarch->runahead_available)
- runahead_clear_variables(p_rarch);
- #endif
- if (hwr)
- memset(hwr, 0, sizeof(*hwr));
- break;
- }
- case CMD_EVENT_CORE_INIT:
- {
- enum rarch_core_type *type = (enum rarch_core_type*)data;
- rarch_system_info_t *sys_info = &p_rarch->runloop_system;
- content_reset_savestate_backups();
- /* Ensure that disk control interface is reset */
- if (sys_info)
- disk_control_set_ext_callback(&sys_info->disk_control, NULL);
- if (!type || !command_event_init_core(settings, p_rarch, *type))
- return false;
- }
- break;
- case CMD_EVENT_VIDEO_APPLY_STATE_CHANGES:
- video_driver_apply_state_changes();
- break;
- case CMD_EVENT_VIDEO_SET_BLOCKING_STATE:
- {
- bool adaptive_vsync = settings->bools.video_adaptive_vsync;
- unsigned swap_interval = settings->uints.video_swap_interval;
- if (p_rarch->current_video->set_nonblock_state)
- p_rarch->current_video->set_nonblock_state(
- p_rarch->video_driver_data, false,
- video_driver_test_all_flags(
- GFX_CTX_FLAGS_ADAPTIVE_VSYNC) &&
- adaptive_vsync, swap_interval);
- }
- break;
- case CMD_EVENT_VIDEO_SET_ASPECT_RATIO:
- video_driver_set_aspect_ratio();
- break;
- case CMD_EVENT_OVERLAY_SET_SCALE_FACTOR:
- #ifdef HAVE_OVERLAY
- {
- overlay_layout_desc_t layout_desc;
- layout_desc.scale_landscape = settings->floats.input_overlay_scale_landscape;
- layout_desc.aspect_adjust_landscape = settings->floats.input_overlay_aspect_adjust_landscape;
- layout_desc.x_separation_landscape = settings->floats.input_overlay_x_separation_landscape;
- layout_desc.y_separation_landscape = settings->floats.input_overlay_y_separation_landscape;
- layout_desc.x_offset_landscape = settings->floats.input_overlay_x_offset_landscape;
- layout_desc.y_offset_landscape = settings->floats.input_overlay_y_offset_landscape;
- layout_desc.scale_portrait = settings->floats.input_overlay_scale_portrait;
- layout_desc.aspect_adjust_portrait = settings->floats.input_overlay_aspect_adjust_portrait;
- layout_desc.x_separation_portrait = settings->floats.input_overlay_x_separation_portrait;
- layout_desc.y_separation_portrait = settings->floats.input_overlay_y_separation_portrait;
- layout_desc.x_offset_portrait = settings->floats.input_overlay_x_offset_portrait;
- layout_desc.y_offset_portrait = settings->floats.input_overlay_y_offset_portrait;
- layout_desc.auto_scale = settings->bools.input_overlay_auto_scale;
- input_overlay_set_scale_factor(p_rarch, p_rarch->overlay_ptr, &layout_desc);
- }
- #endif
- break;
- case CMD_EVENT_OVERLAY_SET_ALPHA_MOD:
- /* Sets a modulating factor for alpha channel. Default is 1.0.
- * The alpha factor is applied for all overlays. */
- #ifdef HAVE_OVERLAY
- {
- float input_overlay_opacity = settings->floats.input_overlay_opacity;
- input_overlay_set_alpha_mod(p_rarch,
- p_rarch->overlay_ptr, input_overlay_opacity);
- }
- #endif
- break;
- case CMD_EVENT_AUDIO_REINIT:
- driver_uninit(p_rarch, DRIVER_AUDIO_MASK);
- drivers_init(p_rarch, DRIVER_AUDIO_MASK);
- break;
- case CMD_EVENT_SHUTDOWN:
- #if defined(__linux__) && !defined(ANDROID)
- runloop_msg_queue_push(msg_hash_to_str(MSG_VALUE_SHUTTING_DOWN), 1, 180, true, NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO);
- command_event(CMD_EVENT_MENU_SAVE_CURRENT_CONFIG, NULL);
- command_event(CMD_EVENT_QUIT, NULL);
- system("shutdown -P now");
- #endif
- break;
- case CMD_EVENT_REBOOT:
- #if defined(__linux__) && !defined(ANDROID)
- runloop_msg_queue_push(msg_hash_to_str(MSG_VALUE_REBOOTING), 1, 180, true, NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO);
- command_event(CMD_EVENT_MENU_SAVE_CURRENT_CONFIG, NULL);
- command_event(CMD_EVENT_QUIT, NULL);
- system("shutdown -r now");
- #endif
- break;
- case CMD_EVENT_RESUME:
- retroarch_menu_running_finished(false);
- if (p_rarch->main_ui_companion_is_on_foreground)
- ui_companion_driver_toggle(settings, p_rarch, false);
- break;
- case CMD_EVENT_ADD_TO_FAVORITES:
- {
- struct string_list *str_list = (struct string_list*)data;
- /* Check whether favourties playlist is at capacity */
- if (playlist_size(g_defaults.content_favorites) >=
- playlist_capacity(g_defaults.content_favorites))
- {
- runloop_msg_queue_push(
- msg_hash_to_str(MSG_ADD_TO_FAVORITES_FAILED), 1, 180, true, NULL,
- MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_ERROR);
- return false;
- }
- if (str_list)
- {
- if (str_list->size >= 6)
- {
- struct playlist_entry entry = {0};
- bool playlist_sort_alphabetical = settings->bools.playlist_sort_alphabetical;
- entry.path = str_list->elems[0].data; /* content_path */
- entry.label = str_list->elems[1].data; /* content_label */
- entry.core_path = str_list->elems[2].data; /* core_path */
- entry.core_name = str_list->elems[3].data; /* core_name */
- entry.crc32 = str_list->elems[4].data; /* crc32 */
- entry.db_name = str_list->elems[5].data; /* db_name */
- /* Write playlist entry */
- if (playlist_push(g_defaults.content_favorites, &entry))
- {
- enum playlist_sort_mode current_sort_mode =
- playlist_get_sort_mode(g_defaults.content_favorites);
- /* New addition - need to resort if option is enabled */
- if ((playlist_sort_alphabetical && (current_sort_mode == PLAYLIST_SORT_MODE_DEFAULT)) ||
- (current_sort_mode == PLAYLIST_SORT_MODE_ALPHABETICAL))
- playlist_qsort(g_defaults.content_favorites);
- playlist_write_file(g_defaults.content_favorites);
- runloop_msg_queue_push(msg_hash_to_str(MSG_ADDED_TO_FAVORITES), 1, 180, true, NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO);
- }
- }
- }
- break;
- }
- case CMD_EVENT_RESET_CORE_ASSOCIATION:
- {
- const char *core_name = "DETECT";
- const char *core_path = "DETECT";
- size_t *playlist_index = (size_t*)data;
- struct playlist_entry entry = {0};
- /* the update function reads our entry as const,
- * so these casts are safe */
- entry.core_path = (char*)core_path;
- entry.core_name = (char*)core_name;
- command_playlist_update_write(
- NULL, *playlist_index, &entry);
- runloop_msg_queue_push(msg_hash_to_str(MSG_RESET_CORE_ASSOCIATION), 1, 180, true, NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO);
- break;
- }
- case CMD_EVENT_RESTART_RETROARCH:
- if (!frontend_driver_set_fork(FRONTEND_FORK_RESTART))
- return false;
- #ifndef HAVE_DYNAMIC
- command_event(CMD_EVENT_QUIT, NULL);
- #endif
- break;
- case CMD_EVENT_MENU_RESET_TO_DEFAULT_CONFIG:
- config_set_defaults(&p_rarch->g_extern);
- break;
- case CMD_EVENT_MENU_SAVE_CURRENT_CONFIG:
- #if !defined(HAVE_DYNAMIC)
- config_save_file_salamander();
- #endif
- #ifdef HAVE_CONFIGFILE
- command_event_save_current_config(p_rarch, OVERRIDE_NONE);
- #endif
- break;
- case CMD_EVENT_MENU_SAVE_CURRENT_CONFIG_OVERRIDE_CORE:
- #ifdef HAVE_CONFIGFILE
- command_event_save_current_config(p_rarch, OVERRIDE_CORE);
- #endif
- break;
- case CMD_EVENT_MENU_SAVE_CURRENT_CONFIG_OVERRIDE_CONTENT_DIR:
- #ifdef HAVE_CONFIGFILE
- command_event_save_current_config(p_rarch, OVERRIDE_CONTENT_DIR);
- #endif
- break;
- case CMD_EVENT_MENU_SAVE_CURRENT_CONFIG_OVERRIDE_GAME:
- #ifdef HAVE_CONFIGFILE
- command_event_save_current_config(p_rarch, OVERRIDE_GAME);
- #endif
- break;
- case CMD_EVENT_MENU_SAVE_CONFIG:
- #ifdef HAVE_CONFIGFILE
- if (!command_event_save_core_config(p_rarch,
- settings->paths.directory_menu_config))
- return false;
- #endif
- break;
- case CMD_EVENT_SHADER_PRESET_LOADED:
- ui_companion_event_command(cmd);
- break;
- case CMD_EVENT_SHADERS_APPLY_CHANGES:
- #ifdef HAVE_MENU
- #if defined(HAVE_CG) || defined(HAVE_GLSL) || defined(HAVE_SLANG) || defined(HAVE_HLSL)
- menu_shader_manager_apply_changes(menu_shader_get(),
- settings->paths.directory_video_shader,
- settings->paths.directory_menu_config
- );
- #endif
- #endif
- ui_companion_event_command(cmd);
- break;
- case CMD_EVENT_PAUSE_TOGGLE:
- boolean = p_rarch->runloop_paused;
- boolean = !boolean;
- #ifdef HAVE_ACCESSIBILITY
- if (is_accessibility_enabled(p_rarch))
- {
- if (boolean)
- accessibility_speak_priority(p_rarch,
- (char*) msg_hash_to_str(MSG_PAUSED), 10);
- else
- accessibility_speak_priority(p_rarch,
- (char*) msg_hash_to_str(MSG_UNPAUSED), 10);
- }
- #endif
- p_rarch->runloop_paused = boolean;
- retroarch_pause_checks(p_rarch);
- break;
- case CMD_EVENT_UNPAUSE:
- boolean = false;
- p_rarch->runloop_paused = boolean;
- retroarch_pause_checks(p_rarch);
- break;
- case CMD_EVENT_PAUSE:
- boolean = true;
- p_rarch->runloop_paused = boolean;
- retroarch_pause_checks(p_rarch);
- break;
- case CMD_EVENT_MENU_PAUSE_LIBRETRO:
- #ifdef HAVE_MENU
- if (p_rarch->menu_driver_alive)
- {
- bool menu_pause_libretro = settings->bools.menu_pause_libretro;
- if (menu_pause_libretro)
- command_event(CMD_EVENT_AUDIO_STOP, NULL);
- else
- command_event(CMD_EVENT_AUDIO_START, NULL);
- }
- else
- {
- bool menu_pause_libretro = settings->bools.menu_pause_libretro;
- if (menu_pause_libretro)
- command_event(CMD_EVENT_AUDIO_START, NULL);
- }
- #endif
- break;
- #ifdef HAVE_NETWORKING
- case CMD_EVENT_NETPLAY_GAME_WATCH:
- netplay_driver_ctl(RARCH_NETPLAY_CTL_GAME_WATCH, NULL);
- break;
- case CMD_EVENT_NETPLAY_DEINIT:
- deinit_netplay(p_rarch);
- break;
- case CMD_EVENT_NETWORK_INIT:
- network_init();
- break;
- /* init netplay manually */
- case CMD_EVENT_NETPLAY_INIT:
- {
- char *hostname = (char*)data;
- const char *netplay_server = settings->paths.netplay_server;
- unsigned netplay_port = settings->uints.netplay_port;
- command_event(CMD_EVENT_NETPLAY_DEINIT, NULL);
- if (!init_netplay(p_rarch,
- NULL,
- hostname
- ? hostname
- : netplay_server, netplay_port))
- {
- command_event(CMD_EVENT_NETPLAY_DEINIT, NULL);
- return false;
- }
- /* Disable rewind & SRAM autosave if it was enabled
- * TODO/FIXME: Add a setting for these tweaks */
- #ifdef HAVE_REWIND
- state_manager_event_deinit(&p_rarch->rewind_st);
- #endif
- #ifdef HAVE_THREADS
- autosave_deinit();
- #endif
- }
- break;
- /* Initialize netplay via lobby when content is loaded */
- case CMD_EVENT_NETPLAY_INIT_DIRECT:
- {
- /* buf is expected to be address|port */
- static struct string_list *hostname = NULL;
- char *buf = (char *)data;
- unsigned netplay_port = settings->uints.netplay_port;
- hostname = string_split(buf, "|");
- command_event(CMD_EVENT_NETPLAY_DEINIT, NULL);
- RARCH_LOG("[Netplay]: Connecting to %s:%d (direct)\n",
- hostname->elems[0].data, !string_is_empty(hostname->elems[1].data)
- ? atoi(hostname->elems[1].data)
- : netplay_port);
- if (!init_netplay(
- p_rarch,
- NULL,
- hostname->elems[0].data,
- !string_is_empty(hostname->elems[1].data)
- ? atoi(hostname->elems[1].data)
- : netplay_port))
- {
- command_event(CMD_EVENT_NETPLAY_DEINIT, NULL);
- string_list_free(hostname);
- return false;
- }
- string_list_free(hostname);
- /* Disable rewind if it was enabled
- TODO/FIXME: Add a setting for these tweaks */
- #ifdef HAVE_REWIND
- state_manager_event_deinit(&p_rarch->rewind_st);
- #endif
- #ifdef HAVE_THREADS
- autosave_deinit();
- #endif
- }
- break;
- /* init netplay via lobby when content is not loaded */
- case CMD_EVENT_NETPLAY_INIT_DIRECT_DEFERRED:
- {
- static struct string_list *hostname = NULL;
- /* buf is expected to be address|port */
- char *buf = (char *)data;
- unsigned netplay_port = settings->uints.netplay_port;
- hostname = string_split(buf, "|");
- command_event(CMD_EVENT_NETPLAY_DEINIT, NULL);
- RARCH_LOG("[Netplay]: Connecting to %s:%d (deferred)\n",
- hostname->elems[0].data, !string_is_empty(hostname->elems[1].data)
- ? atoi(hostname->elems[1].data)
- : netplay_port);
- if (!init_netplay_deferred(p_rarch,
- hostname->elems[0].data,
- !string_is_empty(hostname->elems[1].data)
- ? atoi(hostname->elems[1].data)
- : netplay_port))
- {
- command_event(CMD_EVENT_NETPLAY_DEINIT, NULL);
- string_list_free(hostname);
- return false;
- }
- string_list_free(hostname);
- /* Disable rewind if it was enabled
- * TODO/FIXME: Add a setting for these tweaks */
- #ifdef HAVE_REWIND
- state_manager_event_deinit(&p_rarch->rewind_st);
- #endif
- #ifdef HAVE_THREADS
- autosave_deinit();
- #endif
- }
- break;
- case CMD_EVENT_NETPLAY_ENABLE_HOST:
- {
- #ifdef HAVE_MENU
- bool contentless = false;
- bool is_inited = false;
- content_get_status(&contentless, &is_inited);
- if (netplay_driver_ctl(RARCH_NETPLAY_CTL_IS_DATA_INITED, NULL))
- command_event(CMD_EVENT_NETPLAY_DEINIT, NULL);
- netplay_driver_ctl(RARCH_NETPLAY_CTL_ENABLE_SERVER, NULL);
- /* If we haven't yet started, this will load on its own */
- if (!is_inited)
- {
- runloop_msg_queue_push(
- msg_hash_to_str(MENU_ENUM_LABEL_VALUE_NETPLAY_START_WHEN_LOADED),
- 1, 480, true,
- NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO);
- return false;
- }
- /* Enable Netplay itself */
- if (!command_event(CMD_EVENT_NETPLAY_INIT, NULL))
- return false;
- #endif
- break;
- }
- case CMD_EVENT_NETPLAY_DISCONNECT:
- {
- netplay_driver_ctl(RARCH_NETPLAY_CTL_DISCONNECT, NULL);
- netplay_driver_ctl(RARCH_NETPLAY_CTL_DISABLE, NULL);
- {
- bool rewind_enable = settings->bools.rewind_enable;
- unsigned autosave_interval = settings->uints.autosave_interval;
- #ifdef HAVE_REWIND
- /* Re-enable rewind if it was enabled
- * TODO/FIXME: Add a setting for these tweaks */
- if (rewind_enable)
- command_event(CMD_EVENT_REWIND_INIT, NULL);
- #endif
- if (autosave_interval != 0)
- command_event(CMD_EVENT_AUTOSAVE_INIT, NULL);
- }
- break;
- }
- case CMD_EVENT_NETPLAY_HOST_TOGGLE:
- if (netplay_driver_ctl(RARCH_NETPLAY_CTL_IS_ENABLED, NULL) &&
- netplay_driver_ctl(RARCH_NETPLAY_CTL_IS_SERVER, NULL))
- command_event(CMD_EVENT_NETPLAY_DISCONNECT, NULL);
- else if (netplay_driver_ctl(RARCH_NETPLAY_CTL_IS_ENABLED, NULL) &&
- !netplay_driver_ctl(RARCH_NETPLAY_CTL_IS_SERVER, NULL) &&
- netplay_driver_ctl(RARCH_NETPLAY_CTL_IS_CONNECTED, NULL))
- command_event(CMD_EVENT_NETPLAY_DISCONNECT, NULL);
- else
- command_event(CMD_EVENT_NETPLAY_ENABLE_HOST, NULL);
- break;
- #else
- case CMD_EVENT_NETPLAY_DEINIT:
- case CMD_EVENT_NETWORK_INIT:
- case CMD_EVENT_NETPLAY_INIT:
- case CMD_EVENT_NETPLAY_INIT_DIRECT:
- case CMD_EVENT_NETPLAY_INIT_DIRECT_DEFERRED:
- case CMD_EVENT_NETPLAY_HOST_TOGGLE:
- case CMD_EVENT_NETPLAY_DISCONNECT:
- case CMD_EVENT_NETPLAY_ENABLE_HOST:
- case CMD_EVENT_NETPLAY_GAME_WATCH:
- return false;
- #endif
- case CMD_EVENT_FULLSCREEN_TOGGLE:
- {
- bool *userdata = (bool*)data;
- bool video_fullscreen = settings->bools.video_fullscreen;
- bool ra_is_forced_fs = p_rarch->rarch_force_fullscreen;
- bool new_fullscreen_state = !video_fullscreen && !ra_is_forced_fs;
- if (!video_driver_has_windowed())
- return false;
- p_rarch->rarch_is_switching_display_mode = true;
- /* we toggled manually, write the new value to settings */
- configuration_set_bool(settings, settings->bools.video_fullscreen,
- new_fullscreen_state);
- /* Need to grab this setting's value again */
- video_fullscreen = new_fullscreen_state;
- /* we toggled manually, the CLI arg is irrelevant now */
- if (ra_is_forced_fs)
- p_rarch->rarch_force_fullscreen = false;
- /* If we go fullscreen we drop all drivers and
- * reinitialize to be safe. */
- command_event(CMD_EVENT_REINIT, NULL);
- if (video_fullscreen)
- {
- video_driver_hide_mouse();
- if (!settings->bools.video_windowed_fullscreen)
- input_driver_grab_mouse(p_rarch);
- }
- else
- {
- video_driver_show_mouse();
- if (!settings->bools.video_windowed_fullscreen)
- input_driver_ungrab_mouse(p_rarch);
- }
- p_rarch->rarch_is_switching_display_mode = false;
- if (userdata && *userdata == true)
- video_driver_cached_frame();
- }
- break;
- case CMD_EVENT_LOG_FILE_DEINIT:
- retro_main_log_file_deinit();
- break;
- case CMD_EVENT_DISK_APPEND_IMAGE:
- {
- const char *path = (const char*)data;
- rarch_system_info_t *sys_info = &p_rarch->runloop_system;
- if (string_is_empty(path) || !sys_info)
- return false;
- if (disk_control_enabled(&sys_info->disk_control))
- {
- bool success = false;
- #if defined(HAVE_MENU)
- bool refresh = false;
- /* Get initial disk eject state */
- bool initial_disk_ejected = disk_control_get_eject_state(&sys_info->disk_control);
- #endif
- /* Append disk image */
- success = command_event_disk_control_append_image(p_rarch, path);
- #if defined(HAVE_MENU)
- /* Appending a disk image may or may not affect
- * the disk tray eject status. If status has changed,
- * must refresh the disk options menu */
- if (initial_disk_ejected != disk_control_get_eject_state(&sys_info->disk_control))
- {
- menu_entries_ctl(MENU_ENTRIES_CTL_SET_REFRESH, &refresh);
- menu_driver_ctl(RARCH_MENU_CTL_SET_PREVENT_POPULATE, NULL);
- }
- #endif
- return success;
- }
- else
- runloop_msg_queue_push(
- msg_hash_to_str(MSG_CORE_DOES_NOT_SUPPORT_DISK_OPTIONS),
- 1, 120, true,
- NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO);
- }
- break;
- case CMD_EVENT_DISK_EJECT_TOGGLE:
- {
- rarch_system_info_t *sys_info = &p_rarch->runloop_system;
- bool *show_msg = (bool*)data;
- if (!sys_info)
- return false;
- if (disk_control_enabled(&sys_info->disk_control))
- {
- bool eject = !disk_control_get_eject_state(&sys_info->disk_control);
- bool verbose = true;
- bool refresh = false;
- if (show_msg)
- verbose = *show_msg;
- disk_control_set_eject_state(&sys_info->disk_control, eject, verbose);
- #if defined(HAVE_MENU)
- /* It is necessary to refresh the disk options
- * menu when toggling the tray state */
- menu_entries_ctl(MENU_ENTRIES_CTL_SET_REFRESH, &refresh);
- menu_driver_ctl(RARCH_MENU_CTL_SET_PREVENT_POPULATE, NULL);
- #endif
- }
- else
- runloop_msg_queue_push(
- msg_hash_to_str(MSG_CORE_DOES_NOT_SUPPORT_DISK_OPTIONS),
- 1, 120, true,
- NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO);
- }
- break;
- case CMD_EVENT_DISK_NEXT:
- {
- rarch_system_info_t *sys_info = &p_rarch->runloop_system;
- bool *show_msg = (bool*)data;
- if (!sys_info)
- return false;
- if (disk_control_enabled(&sys_info->disk_control))
- {
- bool verbose = true;
- if (show_msg)
- verbose = *show_msg;
- disk_control_set_index_next(&sys_info->disk_control, verbose);
- }
- else
- runloop_msg_queue_push(
- msg_hash_to_str(MSG_CORE_DOES_NOT_SUPPORT_DISK_OPTIONS),
- 1, 120, true,
- NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO);
- }
- break;
- case CMD_EVENT_DISK_PREV:
- {
- rarch_system_info_t *sys_info = &p_rarch->runloop_system;
- bool *show_msg = (bool*)data;
- if (!sys_info)
- return false;
- if (disk_control_enabled(&sys_info->disk_control))
- {
- bool verbose = true;
- if (show_msg)
- verbose = *show_msg;
- disk_control_set_index_prev(&sys_info->disk_control, verbose);
- }
- else
- runloop_msg_queue_push(
- msg_hash_to_str(MSG_CORE_DOES_NOT_SUPPORT_DISK_OPTIONS),
- 1, 120, true,
- NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO);
- }
- break;
- case CMD_EVENT_DISK_INDEX:
- {
- rarch_system_info_t *sys_info = &p_rarch->runloop_system;
- unsigned *index = (unsigned*)data;
- if (!sys_info || !index)
- return false;
- /* Note: Menu itself provides visual feedback - no
- * need to print info message to screen */
- if (disk_control_enabled(&sys_info->disk_control))
- disk_control_set_index(&sys_info->disk_control, *index, false);
- else
- runloop_msg_queue_push(
- msg_hash_to_str(MSG_CORE_DOES_NOT_SUPPORT_DISK_OPTIONS),
- 1, 120, true,
- NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO);
- }
- break;
- case CMD_EVENT_RUMBLE_STOP:
- {
- unsigned i;
- for (i = 0; i < MAX_USERS; i++)
- {
- input_driver_set_rumble_state(i, RETRO_RUMBLE_STRONG, 0);
- input_driver_set_rumble_state(i, RETRO_RUMBLE_WEAK, 0);
- }
- }
- break;
- case CMD_EVENT_GRAB_MOUSE_TOGGLE:
- {
- bool ret = false;
- bool grab_mouse_state = p_rarch->input_driver_grab_mouse_state;
- grab_mouse_state = !grab_mouse_state;
- if (grab_mouse_state)
- ret = input_driver_grab_mouse(p_rarch);
- else
- ret = input_driver_ungrab_mouse(p_rarch);
- if (!ret)
- return false;
- RARCH_LOG("[Input]: %s => %s\n",
- msg_hash_to_str(MSG_GRAB_MOUSE_STATE),
- grab_mouse_state ? "ON" : "OFF");
- if (grab_mouse_state)
- video_driver_hide_mouse();
- else
- video_driver_show_mouse();
- }
- break;
- case CMD_EVENT_UI_COMPANION_TOGGLE:
- ui_companion_driver_toggle(settings, p_rarch, true);
- break;
- case CMD_EVENT_GAME_FOCUS_TOGGLE:
- {
- bool video_fullscreen =
- settings->bools.video_fullscreen || p_rarch->rarch_force_fullscreen;
- enum input_game_focus_cmd_type game_focus_cmd = GAME_FOCUS_CMD_TOGGLE;
- bool current_enable_state = p_rarch->game_focus_state.enabled;
- bool apply_update = false;
- bool show_message = false;
- if (data)
- game_focus_cmd = *((enum input_game_focus_cmd_type*)data);
- switch (game_focus_cmd)
- {
- case GAME_FOCUS_CMD_OFF:
- /* Force game focus off */
- p_rarch->game_focus_state.enabled = false;
- if (p_rarch->game_focus_state.enabled != current_enable_state)
- {
- apply_update = true;
- show_message = true;
- }
- break;
- case GAME_FOCUS_CMD_ON:
- /* Force game focus on */
- p_rarch->game_focus_state.enabled = true;
- if (p_rarch->game_focus_state.enabled != current_enable_state)
- {
- apply_update = true;
- show_message = true;
- }
- break;
- case GAME_FOCUS_CMD_TOGGLE:
- /* Invert current game focus state */
- p_rarch->game_focus_state.enabled = !p_rarch->game_focus_state.enabled;
- #ifdef HAVE_MENU
- /* If menu is currently active, disable
- * 'toggle on' functionality */
- if (p_rarch->menu_driver_alive)
- p_rarch->game_focus_state.enabled = false;
- #endif
- if (p_rarch->game_focus_state.enabled != current_enable_state)
- {
- apply_update = true;
- show_message = true;
- }
- break;
- case GAME_FOCUS_CMD_REAPPLY:
- /* Reapply current game focus state */
- apply_update = true;
- show_message = false;
- break;
- default:
- break;
- }
- if (apply_update)
- {
- if (p_rarch->game_focus_state.enabled)
- {
- input_driver_grab_mouse(p_rarch);
- video_driver_hide_mouse();
- }
- else if (!video_fullscreen)
- {
- input_driver_ungrab_mouse(p_rarch);
- video_driver_show_mouse();
- }
- p_rarch->input_driver_block_hotkey = p_rarch->game_focus_state.enabled;
- p_rarch->keyboard_mapping_blocked = p_rarch->game_focus_state.enabled;
- if (show_message)
- runloop_msg_queue_push(
- p_rarch->game_focus_state.enabled ?
- msg_hash_to_str(MSG_GAME_FOCUS_ON) :
- msg_hash_to_str(MSG_GAME_FOCUS_OFF),
- 1, 60, true,
- NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO);
- RARCH_LOG("[Input]: %s => %s\n",
- "Game Focus",
- p_rarch->game_focus_state.enabled ? "ON" : "OFF");
- }
- }
- break;
- case CMD_EVENT_VOLUME_UP:
- command_event_set_volume(settings, p_rarch, 0.5f);
- break;
- case CMD_EVENT_VOLUME_DOWN:
- command_event_set_volume(settings, p_rarch, -0.5f);
- break;
- case CMD_EVENT_MIXER_VOLUME_UP:
- command_event_set_mixer_volume(settings, p_rarch, 0.5f);
- break;
- case CMD_EVENT_MIXER_VOLUME_DOWN:
- command_event_set_mixer_volume(settings, p_rarch, -0.5f);
- break;
- case CMD_EVENT_SET_FRAME_LIMIT:
- retroarch_set_frame_limit(p_rarch,
- settings->floats.fastforward_ratio);
- break;
- case CMD_EVENT_DISCORD_INIT:
- #ifdef HAVE_DISCORD
- {
- bool discord_enable = settings ? settings->bools.discord_enable : false;
- const char *discord_app_id = settings ? settings->arrays.discord_app_id : NULL;
- discord_state_t *discord_st = &p_rarch->discord_st;
- if (!settings)
- return false;
- if (!discord_enable)
- return false;
- if (discord_st->ready)
- return true;
- discord_init(discord_st,
- discord_app_id,
- p_rarch->launch_arguments);
- }
- #endif
- break;
- case CMD_EVENT_DISCORD_UPDATE:
- {
- #ifdef HAVE_DISCORD
- discord_state_t *discord_st = &p_rarch->discord_st;
- if (!data || !discord_st->ready)
- return false;
- discord_userdata_t *userdata = (discord_userdata_t*)data;
- if (discord_st->ready)
- discord_update(userdata->status);
- #endif
- }
- break;
- case CMD_EVENT_AI_SERVICE_CALL:
- {
- #ifdef HAVE_TRANSLATE
- unsigned ai_service_mode = settings->uints.ai_service_mode;
- if (ai_service_mode == 1 && is_ai_service_speech_running())
- {
- ai_service_speech_stop();
- #ifdef HAVE_ACCESSIBILITY
- if (is_accessibility_enabled(p_rarch))
- accessibility_speak_priority(p_rarch, "stopped.", 10);
- #endif
- }
- #ifdef HAVE_ACCESSIBILITY
- else if (is_accessibility_enabled(p_rarch) &&
- ai_service_mode == 2 &&
- is_narrator_running(p_rarch))
- accessibility_speak_priority(p_rarch, "stopped.", 10);
- #endif
- else
- {
- bool paused = p_rarch->runloop_paused;
- if (data)
- paused = *((bool*)data);
- if (p_rarch->ai_service_auto == 0 && !settings->bools.ai_service_pause)
- p_rarch->ai_service_auto = 1;
- if (p_rarch->ai_service_auto != 2)
- RARCH_LOG("AI Service Called...\n");
- run_translation_service(p_rarch->configuration_settings,
- p_rarch, paused);
- }
- #endif
- break;
- }
- case CMD_EVENT_NONE:
- return false;
- }
- return true;
- }
- /* FRONTEND */
- void retroarch_override_setting_set(
- enum rarch_override_setting enum_idx, void *data)
- {
- struct rarch_state *p_rarch = &rarch_st;
- switch (enum_idx)
- {
- case RARCH_OVERRIDE_SETTING_LIBRETRO_DEVICE:
- {
- unsigned *val = (unsigned*)data;
- if (val)
- {
- unsigned bit = *val;
- BIT256_SET(p_rarch->has_set_libretro_device, bit);
- }
- }
- break;
- case RARCH_OVERRIDE_SETTING_VERBOSITY:
- p_rarch->has_set_verbosity = true;
- break;
- case RARCH_OVERRIDE_SETTING_LIBRETRO:
- p_rarch->has_set_libretro = true;
- break;
- case RARCH_OVERRIDE_SETTING_LIBRETRO_DIRECTORY:
- p_rarch->has_set_libretro_directory = true;
- break;
- case RARCH_OVERRIDE_SETTING_SAVE_PATH:
- p_rarch->has_set_save_path = true;
- break;
- case RARCH_OVERRIDE_SETTING_STATE_PATH:
- p_rarch->has_set_state_path = true;
- break;
- #ifdef HAVE_NETWORKING
- case RARCH_OVERRIDE_SETTING_NETPLAY_MODE:
- p_rarch->has_set_netplay_mode = true;
- break;
- case RARCH_OVERRIDE_SETTING_NETPLAY_IP_ADDRESS:
- p_rarch->has_set_netplay_ip_address = true;
- break;
- case RARCH_OVERRIDE_SETTING_NETPLAY_IP_PORT:
- p_rarch->has_set_netplay_ip_port = true;
- break;
- case RARCH_OVERRIDE_SETTING_NETPLAY_STATELESS_MODE:
- p_rarch->has_set_netplay_stateless_mode = true;
- break;
- case RARCH_OVERRIDE_SETTING_NETPLAY_CHECK_FRAMES:
- p_rarch->has_set_netplay_check_frames = true;
- break;
- #endif
- case RARCH_OVERRIDE_SETTING_UPS_PREF:
- #ifdef HAVE_PATCH
- p_rarch->has_set_ups_pref = true;
- #endif
- break;
- case RARCH_OVERRIDE_SETTING_BPS_PREF:
- #ifdef HAVE_PATCH
- p_rarch->has_set_bps_pref = true;
- #endif
- break;
- case RARCH_OVERRIDE_SETTING_IPS_PREF:
- #ifdef HAVE_PATCH
- p_rarch->has_set_ips_pref = true;
- #endif
- break;
- case RARCH_OVERRIDE_SETTING_LOG_TO_FILE:
- p_rarch->has_set_log_to_file = true;
- break;
- case RARCH_OVERRIDE_SETTING_NONE:
- default:
- break;
- }
- }
- void retroarch_override_setting_unset(
- enum rarch_override_setting enum_idx, void *data)
- {
- struct rarch_state *p_rarch = &rarch_st;
- switch (enum_idx)
- {
- case RARCH_OVERRIDE_SETTING_LIBRETRO_DEVICE:
- {
- unsigned *val = (unsigned*)data;
- if (val)
- {
- unsigned bit = *val;
- BIT256_CLEAR(p_rarch->has_set_libretro_device, bit);
- }
- }
- break;
- case RARCH_OVERRIDE_SETTING_VERBOSITY:
- p_rarch->has_set_verbosity = false;
- break;
- case RARCH_OVERRIDE_SETTING_LIBRETRO:
- p_rarch->has_set_libretro = false;
- break;
- case RARCH_OVERRIDE_SETTING_LIBRETRO_DIRECTORY:
- p_rarch->has_set_libretro_directory = false;
- break;
- case RARCH_OVERRIDE_SETTING_SAVE_PATH:
- p_rarch->has_set_save_path = false;
- break;
- case RARCH_OVERRIDE_SETTING_STATE_PATH:
- p_rarch->has_set_state_path = false;
- break;
- #ifdef HAVE_NETWORKING
- case RARCH_OVERRIDE_SETTING_NETPLAY_MODE:
- p_rarch->has_set_netplay_mode = false;
- break;
- case RARCH_OVERRIDE_SETTING_NETPLAY_IP_ADDRESS:
- p_rarch->has_set_netplay_ip_address = false;
- break;
- case RARCH_OVERRIDE_SETTING_NETPLAY_IP_PORT:
- p_rarch->has_set_netplay_ip_port = false;
- break;
- case RARCH_OVERRIDE_SETTING_NETPLAY_STATELESS_MODE:
- p_rarch->has_set_netplay_stateless_mode = false;
- break;
- case RARCH_OVERRIDE_SETTING_NETPLAY_CHECK_FRAMES:
- p_rarch->has_set_netplay_check_frames = false;
- break;
- #endif
- case RARCH_OVERRIDE_SETTING_UPS_PREF:
- #ifdef HAVE_PATCH
- p_rarch->has_set_ups_pref = false;
- #endif
- break;
- case RARCH_OVERRIDE_SETTING_BPS_PREF:
- #ifdef HAVE_PATCH
- p_rarch->has_set_bps_pref = false;
- #endif
- break;
- case RARCH_OVERRIDE_SETTING_IPS_PREF:
- #ifdef HAVE_PATCH
- p_rarch->has_set_ips_pref = false;
- #endif
- break;
- case RARCH_OVERRIDE_SETTING_LOG_TO_FILE:
- p_rarch->has_set_log_to_file = false;
- break;
- case RARCH_OVERRIDE_SETTING_NONE:
- default:
- break;
- }
- }
- static void retroarch_override_setting_free_state(void)
- {
- unsigned i;
- for (i = 0; i < RARCH_OVERRIDE_SETTING_LAST; i++)
- {
- if (i == RARCH_OVERRIDE_SETTING_LIBRETRO_DEVICE)
- {
- unsigned j;
- for (j = 0; j < MAX_USERS; j++)
- retroarch_override_setting_unset(
- (enum rarch_override_setting)(i), &j);
- }
- else
- retroarch_override_setting_unset(
- (enum rarch_override_setting)(i), NULL);
- }
- }
- static void global_free(struct rarch_state *p_rarch)
- {
- global_t *global = NULL;
- content_deinit();
- path_deinit_subsystem(p_rarch);
- command_event(CMD_EVENT_RECORD_DEINIT, NULL);
- command_event(CMD_EVENT_LOG_FILE_DEINIT, NULL);
- p_rarch->rarch_is_sram_load_disabled = false;
- p_rarch->rarch_is_sram_save_disabled = false;
- p_rarch->rarch_use_sram = false;
- #ifdef HAVE_PATCH
- rarch_ctl(RARCH_CTL_UNSET_BPS_PREF, NULL);
- rarch_ctl(RARCH_CTL_UNSET_IPS_PREF, NULL);
- rarch_ctl(RARCH_CTL_UNSET_UPS_PREF, NULL);
- p_rarch->rarch_patch_blocked = false;
- #endif
- #ifdef HAVE_CONFIGFILE
- p_rarch->rarch_block_config_read = false;
- p_rarch->runloop_overrides_active = false;
- p_rarch->runloop_remaps_core_active = false;
- p_rarch->runloop_remaps_game_active = false;
- p_rarch->runloop_remaps_content_dir_active = false;
- #endif
- p_rarch->current_core.has_set_input_descriptors = false;
- p_rarch->current_core.has_set_subsystems = false;
- global = &p_rarch->g_extern;
- path_clear_all();
- dir_clear_all();
- if (global)
- {
- if (!string_is_empty(global->name.remapfile))
- free(global->name.remapfile);
- memset(global, 0, sizeof(struct global));
- }
- retroarch_override_setting_free_state();
- }
- /**
- * main_exit:
- *
- * Cleanly exit RetroArch.
- *
- * Also saves configuration files to disk,
- * and (optionally) autosave state.
- **/
- void main_exit(void *args)
- {
- struct rarch_state *p_rarch = &rarch_st;
- #ifdef HAVE_MENU
- struct menu_state *menu_st = &p_rarch->menu_driver_state;
- #endif
- settings_t *settings = p_rarch->configuration_settings;
- bool config_save_on_exit = settings->bools.config_save_on_exit;
- video_driver_restore_cached(p_rarch, settings);
- if (config_save_on_exit)
- command_event(CMD_EVENT_MENU_SAVE_CURRENT_CONFIG, NULL);
- #if defined(HAVE_GFX_WIDGETS)
- /* Do not want display widgets to live any more. */
- p_rarch->widgets_persisting = false;
- #endif
- #ifdef HAVE_MENU
- /* Do not want menu context to live any more. */
- if (menu_st)
- menu_st->data_own = false;
- #endif
- rarch_ctl(RARCH_CTL_MAIN_DEINIT, NULL);
- if (p_rarch->runloop_perfcnt_enable)
- {
- RARCH_LOG("[PERF]: Performance counters (RetroArch):\n");
- log_counters(p_rarch->perf_counters_rarch, p_rarch->perf_ptr_rarch);
- }
- #if defined(HAVE_LOGGER) && !defined(ANDROID)
- logger_shutdown();
- #endif
- frontend_driver_deinit(args);
- frontend_driver_exitspawn(
- path_get_ptr(RARCH_PATH_CORE),
- path_get_realsize(RARCH_PATH_CORE),
- p_rarch->launch_arguments);
- p_rarch->has_set_username = false;
- p_rarch->rarch_is_inited = false;
- p_rarch->rarch_error_on_init = false;
- #ifdef HAVE_CONFIGFILE
- p_rarch->rarch_block_config_read = false;
- #endif
- retroarch_msg_queue_deinit(p_rarch);
- driver_uninit(p_rarch, DRIVERS_CMD_ALL);
- command_event(CMD_EVENT_LOG_FILE_DEINIT, NULL);
- rarch_ctl(RARCH_CTL_STATE_FREE, NULL);
- global_free(p_rarch);
- task_queue_deinit();
- if (p_rarch->configuration_settings)
- free(p_rarch->configuration_settings);
- p_rarch->configuration_settings = NULL;
- ui_companion_driver_deinit(p_rarch);
- frontend_driver_shutdown(false);
- retroarch_deinit_drivers(p_rarch, &p_rarch->retro_ctx);
- ui_companion_driver_free();
- frontend_driver_free();
- rtime_deinit();
- #if defined(ANDROID)
- play_feature_delivery_deinit();
- #endif
- #if defined(_WIN32) && !defined(_XBOX) && !defined(__WINRT__)
- CoUninitialize();
- #endif
- }
- /**
- * main_entry:
- *
- * Main function of RetroArch.
- *
- * If HAVE_MAIN is not defined, will contain main loop and will not
- * be exited from until we exit the program. Otherwise, will
- * just do initialization.
- *
- * Returns: varies per platform.
- **/
- int rarch_main(int argc, char *argv[], void *data)
- {
- struct rarch_state *p_rarch = &rarch_st;
- #if defined(HAVE_CG) || defined(HAVE_GLSL) || defined(HAVE_SLANG) || defined(HAVE_HLSL)
- p_rarch->shader_presets_need_reload = true;
- #endif
- #ifdef HAVE_RUNAHEAD
- p_rarch->runahead_video_driver_is_active = true;
- p_rarch->runahead_available = true;
- p_rarch->runahead_secondary_core_available = true;
- p_rarch->runahead_force_input_dirty = true;
- #endif
- #if defined(_WIN32) && !defined(_XBOX) && !defined(__WINRT__)
- if (FAILED(CoInitialize(NULL)))
- {
- RARCH_ERR("FATAL: Failed to initialize the COM interface\n");
- return 1;
- }
- #endif
- rtime_init();
- #if defined(ANDROID)
- play_feature_delivery_init();
- #endif
- libretro_free_system_info(&p_rarch->runloop_system.info);
- command_event(CMD_EVENT_HISTORY_DEINIT, NULL);
- rarch_favorites_deinit();
- p_rarch->configuration_settings = (settings_t*)calloc(1, sizeof(settings_t));
- retroarch_deinit_drivers(p_rarch, &p_rarch->retro_ctx);
- rarch_ctl(RARCH_CTL_STATE_FREE, NULL);
- global_free(p_rarch);
- frontend_driver_init_first(data);
- if (p_rarch->rarch_is_inited)
- driver_uninit(p_rarch, DRIVERS_CMD_ALL);
- #ifdef HAVE_THREAD_STORAGE
- sthread_tls_create(&p_rarch->rarch_tls);
- sthread_tls_set(&p_rarch->rarch_tls, MAGIC_POINTER);
- #endif
- p_rarch->video_driver_active = true;
- p_rarch->audio_driver_active = true;
- {
- uint8_t i;
- for (i = 0; i < MAX_USERS; i++)
- input_config_set_device(i, RETRO_DEVICE_JOYPAD);
- }
- retroarch_msg_queue_init(p_rarch);
- if (frontend_driver_is_inited())
- {
- content_ctx_info_t info;
- info.argc = argc;
- info.argv = argv;
- info.args = data;
- info.environ_get = frontend_driver_environment_get_ptr();
- if (!task_push_load_content_from_cli(
- NULL,
- NULL,
- &info,
- CORE_TYPE_PLAIN,
- NULL,
- NULL))
- return 1;
- }
- ui_companion_driver_init_first(p_rarch->configuration_settings,
- p_rarch);
- #if !defined(HAVE_MAIN) || defined(HAVE_QT)
- for (;;)
- {
- int ret;
- bool app_exit = false;
- #ifdef HAVE_QT
- ui_companion_qt.application->process_events();
- #endif
- ret = runloop_iterate();
- task_queue_check();
- #ifdef HAVE_QT
- app_exit = ui_companion_qt.application->exiting;
- #endif
- if (ret == -1 || app_exit)
- {
- #ifdef HAVE_QT
- ui_companion_qt.application->quit();
- #endif
- break;
- }
- }
- main_exit(data);
- #endif
- return 0;
- }
- #if defined(EMSCRIPTEN)
- void RWebAudioRecalibrateTime(void);
- void emscripten_mainloop(void)
- {
- int ret;
- static unsigned emscripten_frame_count = 0;
- struct rarch_state *p_rarch = &rarch_st;
- settings_t *settings = p_rarch->configuration_settings;
- bool black_frame_insertion = settings->uints.video_black_frame_insertion;
- bool input_driver_nonblock_state = p_rarch->input_driver_nonblock_state;
- bool runloop_is_slowmotion = p_rarch->runloop_slowmotion;
- bool runloop_is_paused = p_rarch->runloop_paused;
- RWebAudioRecalibrateTime();
- emscripten_frame_count++;
- /* Disable BFI during fast forward, slow-motion,
- * and pause to prevent flicker. */
- if (
- black_frame_insertion
- && !input_driver_nonblock_state
- && !runloop_is_slowmotion
- && !runloop_is_paused)
- {
- if ((emscripten_frame_count % (black_frame_insertion+1)) != 0)
- {
- glClear(GL_COLOR_BUFFER_BIT);
- if (p_rarch->current_video_context.swap_buffers)
- p_rarch->current_video_context.swap_buffers(
- p_rarch->video_context_data);
- return;
- }
- }
- ret = runloop_iterate();
- task_queue_check();
- if (ret != -1)
- return;
- main_exit(NULL);
- emscripten_force_exit(0);
- }
- #endif
- #ifndef HAVE_MAIN
- #ifdef __cplusplus
- extern "C"
- #endif
- int main(int argc, char *argv[])
- {
- return rarch_main(argc, argv, NULL);
- }
- #endif
- /* CORE OPTIONS */
- static const char *core_option_manager_parse_value_label(
- const char *value, const char *value_label)
- {
- /* 'value_label' may be NULL */
- const char *label = string_is_empty(value_label) ?
- value : value_label;
- if (string_is_empty(label))
- return NULL;
- /* Any label starting with a digit (or +/-)
- * cannot be a boolean string, and requires
- * no further processing */
- if (ISDIGIT((unsigned char)*label) ||
- (*label == '+') ||
- (*label == '-'))
- return label;
- /* Core devs have a habit of using arbitrary
- * strings to label boolean values (i.e. enabled,
- * Enabled, on, On, ON, true, True, TRUE, disabled,
- * Disabled, off, Off, OFF, false, False, FALSE).
- * These should all be converted to standard ON/OFF
- * strings
- * > Note: We require some duplication here
- * (e.g. MENU_ENUM_LABEL_ENABLED *and*
- * MENU_ENUM_LABEL_VALUE_ENABLED) in order
- * to match both localised and non-localised
- * strings. This function is not performance
- * critical, so these extra comparisons do
- * no harm */
- if (string_is_equal_noncase(label, msg_hash_to_str(MENU_ENUM_LABEL_ENABLED)) ||
- string_is_equal_noncase(label, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_ENABLED)) ||
- string_is_equal_noncase(label, "enable") ||
- string_is_equal_noncase(label, "on") ||
- string_is_equal_noncase(label, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_ON)) ||
- string_is_equal_noncase(label, "true") ||
- string_is_equal_noncase(label, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_TRUE)))
- label = msg_hash_to_str(MENU_ENUM_LABEL_VALUE_ON);
- else if (string_is_equal_noncase(label, msg_hash_to_str(MENU_ENUM_LABEL_DISABLED)) ||
- string_is_equal_noncase(label, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_DISABLED)) ||
- string_is_equal_noncase(label, "disable") ||
- string_is_equal_noncase(label, "off") ||
- string_is_equal_noncase(label, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_OFF)) ||
- string_is_equal_noncase(label, "false") ||
- string_is_equal_noncase(label, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_FALSE)))
- label = msg_hash_to_str(MENU_ENUM_LABEL_VALUE_OFF);
- return label;
- }
- static bool core_option_manager_parse_variable(
- core_option_manager_t *opt, size_t idx,
- const struct retro_variable *var,
- config_file_t *config_src)
- {
- size_t i;
- union string_list_elem_attr attr;
- const char *val_start = NULL;
- char *value = NULL;
- char *desc_end = NULL;
- struct core_option *option = (struct core_option*)&opt->opts[idx];
- struct config_entry_list
- *entry = NULL;
-
- /* All options are visible by default */
- option->visible = true;
- if (!string_is_empty(var->key))
- option->key = strdup(var->key);
- if (!string_is_empty(var->value))
- value = strdup(var->value);
- if (!string_is_empty(value))
- desc_end = strstr(value, "; ");
- if (!desc_end)
- goto error;
- *desc_end = '\0';
- if (!string_is_empty(value))
- option->desc = strdup(value);
- val_start = desc_end + 2;
- option->vals = string_split(val_start, "|");
- if (!option->vals)
- goto error;
- /* Legacy core option interface has no concept
- * of value labels
- * > Use actual values for display purposes */
- attr.i = 0;
- option->val_labels = string_list_new();
- if (!option->val_labels)
- goto error;
- /* > Loop over values and 'extract' labels */
- for (i = 0; i < option->vals->size; i++)
- {
- const char *value = option->vals->elems[i].data;
- const char *value_label = core_option_manager_parse_value_label(
- value, NULL);
- /* Redundant safely check... */
- value_label = string_is_empty(value_label) ?
- value : value_label;
- /* Append value label string */
- string_list_append(option->val_labels, value_label, attr);
- }
- /* Legacy core option interface always uses first
- * defined value as the default */
- option->default_index = 0;
- option->index = 0;
- if (config_src)
- entry = config_get_entry(config_src, option->key);
- else
- entry = config_get_entry(opt->conf, option->key);
- /* Set current config value */
- if (entry && !string_is_empty(entry->value))
- {
- for (i = 0; i < option->vals->size; i++)
- {
- if (string_is_equal(option->vals->elems[i].data, entry->value))
- {
- option->index = i;
- break;
- }
- }
- }
- free(value);
- return true;
- error:
- free(value);
- return false;
- }
- static bool core_option_manager_parse_option(
- core_option_manager_t *opt, size_t idx,
- const struct retro_core_option_definition *option_def,
- config_file_t *config_src)
- {
- size_t i;
- union string_list_elem_attr attr;
- struct config_entry_list
- *entry = NULL;
- size_t num_vals = 0;
- struct core_option *option = (struct core_option*)&opt->opts[idx];
- const struct retro_core_option_value
- *values = option_def->values;
- /* All options are visible by default */
- option->visible = true;
- if (!string_is_empty(option_def->key))
- option->key = strdup(option_def->key);
- if (!string_is_empty(option_def->desc))
- option->desc = strdup(option_def->desc);
- if (!string_is_empty(option_def->info))
- option->info = strdup(option_def->info);
- /* Get number of values */
- for (;;)
- {
- if (string_is_empty(values[num_vals].value))
- break;
- num_vals++;
- }
- if (num_vals < 1)
- return false;
- /* Initialise string lists */
- attr.i = 0;
- option->vals = string_list_new();
- option->val_labels = string_list_new();
- if (!option->vals || !option->val_labels)
- return false;
- /* Initialise default value */
- option->default_index = 0;
- option->index = 0;
- /* Extract value/label pairs */
- for (i = 0; i < num_vals; i++)
- {
- const char *value = values[i].value;
- const char *value_label = values[i].label;
- /* Append value string
- * > We know that 'value' is always valid */
- string_list_append(option->vals, value, attr);
- /* Value label requires additional processing */
- value_label = core_option_manager_parse_value_label(
- value, value_label);
- /* > Redundant safely check... */
- value_label = string_is_empty(value_label) ?
- value : value_label;
- /* Append value label string */
- string_list_append(option->val_labels, value_label, attr);
- /* Check whether this value is the default setting */
- if (!string_is_empty(option_def->default_value))
- {
- if (string_is_equal(option_def->default_value, value))
- {
- option->default_index = i;
- option->index = i;
- }
- }
- }
- if (config_src)
- entry = config_get_entry(config_src, option->key);
- else
- entry = config_get_entry(opt->conf, option->key);
- /* Set current config value */
- if (entry && !string_is_empty(entry->value))
- {
- for (i = 0; i < option->vals->size; i++)
- {
- if (string_is_equal(option->vals->elems[i].data, entry->value))
- {
- option->index = i;
- break;
- }
- }
- }
- return true;
- }
- /**
- * core_option_manager_free:
- * @opt : options manager handle
- *
- * Frees core option manager handle.
- **/
- static void core_option_manager_free(core_option_manager_t *opt)
- {
- size_t i;
- if (!opt)
- return;
- for (i = 0; i < opt->size; i++)
- {
- if (opt->opts[i].desc)
- free(opt->opts[i].desc);
- if (opt->opts[i].info)
- free(opt->opts[i].info);
- if (opt->opts[i].key)
- free(opt->opts[i].key);
- if (opt->opts[i].vals)
- string_list_free(opt->opts[i].vals);
- if (opt->opts[i].val_labels)
- string_list_free(opt->opts[i].val_labels);
- opt->opts[i].desc = NULL;
- opt->opts[i].info = NULL;
- opt->opts[i].key = NULL;
- opt->opts[i].vals = NULL;
- }
- if (opt->conf)
- config_file_free(opt->conf);
- free(opt->opts);
- free(opt);
- }
- /**
- * core_option_manager_new_vars:
- * @conf_path : Filesystem path to write core option config file to.
- * @src_conf_path : Filesystem path from which to load initial config settings.
- * @vars : Pointer to variable array handle.
- *
- * Legacy version of core_option_manager_new().
- * Creates and initializes a core manager handle.
- *
- * Returns: handle to new core manager handle, otherwise NULL.
- **/
- static core_option_manager_t *core_option_manager_new_vars(
- const char *conf_path, const char *src_conf_path,
- const struct retro_variable *vars)
- {
- const struct retro_variable *var;
- size_t size = 0;
- config_file_t *config_src = NULL;
- core_option_manager_t *opt = (core_option_manager_t*)
- malloc(sizeof(*opt));
- if (!opt)
- return NULL;
- opt->conf = NULL;
- opt->conf_path[0] = '\0';
- opt->opts = NULL;
- opt->size = 0;
- opt->updated = false;
- if (!string_is_empty(conf_path))
- if (!(opt->conf = config_file_new_from_path_to_string(conf_path)))
- if (!(opt->conf = config_file_new_alloc()))
- goto error;
- strlcpy(opt->conf_path, conf_path, sizeof(opt->conf_path));
- /* Load source config file, if required */
- if (!string_is_empty(src_conf_path))
- config_src = config_file_new_from_path_to_string(src_conf_path);
- for (var = vars; var->key && var->value; var++)
- size++;
- if (size == 0)
- goto error;
- opt->opts = (struct core_option*)calloc(size, sizeof(*opt->opts));
- if (!opt->opts)
- goto error;
- opt->size = size;
- size = 0;
- for (var = vars; var->key && var->value; size++, var++)
- {
- if (!core_option_manager_parse_variable(opt, size, var, config_src))
- goto error;
- }
- if (config_src)
- config_file_free(config_src);
- return opt;
- error:
- if (config_src)
- config_file_free(config_src);
- core_option_manager_free(opt);
- return NULL;
- }
- /**
- * core_option_manager_new:
- * @conf_path : Filesystem path to write core option config file to.
- * @src_conf_path : Filesystem path from which to load initial config settings.
- * @option_defs : Pointer to variable array handle.
- *
- * Creates and initializes a core manager handle.
- *
- * Returns: handle to new core manager handle, otherwise NULL.
- **/
- static core_option_manager_t *core_option_manager_new(
- const char *conf_path, const char *src_conf_path,
- const struct retro_core_option_definition *option_defs)
- {
- const struct retro_core_option_definition *option_def;
- size_t size = 0;
- config_file_t *config_src = NULL;
- core_option_manager_t *opt = (core_option_manager_t*)
- malloc(sizeof(*opt));
- if (!opt)
- return NULL;
- opt->conf = NULL;
- opt->conf_path[0] = '\0';
- opt->opts = NULL;
- opt->size = 0;
- opt->updated = false;
- if (!string_is_empty(conf_path))
- if (!(opt->conf = config_file_new_from_path_to_string(conf_path)))
- if (!(opt->conf = config_file_new_alloc()))
- goto error;
- strlcpy(opt->conf_path, conf_path, sizeof(opt->conf_path));
- /* Load source config file, if required */
- if (!string_is_empty(src_conf_path))
- config_src = config_file_new_from_path_to_string(src_conf_path);
- /* Note: 'option_def->info == NULL' is valid */
- for (option_def = option_defs;
- option_def->key && option_def->desc && option_def->values[0].value;
- option_def++)
- size++;
- if (size == 0)
- goto error;
- opt->opts = (struct core_option*)calloc(size, sizeof(*opt->opts));
- if (!opt->opts)
- goto error;
- opt->size = size;
- size = 0;
- /* Note: 'option_def->info == NULL' is valid */
- for (option_def = option_defs;
- option_def->key && option_def->desc && option_def->values[0].value;
- size++, option_def++)
- if (!core_option_manager_parse_option(opt, size, option_def, config_src))
- goto error;
- if (config_src)
- config_file_free(config_src);
- return opt;
- error:
- if (config_src)
- config_file_free(config_src);
- core_option_manager_free(opt);
- return NULL;
- }
- /**
- * core_option_manager_flush:
- * @opt : options manager handle
- *
- * Writes core option key-pair values to file.
- **/
- static void core_option_manager_flush(
- config_file_t *conf,
- core_option_manager_t *opt)
- {
- size_t i;
- for (i = 0; i < opt->size; i++)
- {
- struct core_option *option = (struct core_option*)&opt->opts[i];
- if (option)
- config_set_string(conf, option->key,
- opt->opts[i].vals->elems[opt->opts[i].index].data);
- }
- }
- /**
- * core_option_manager_get_desc:
- * @opt : options manager handle
- * @index : index identifier of the option
- *
- * Gets description for an option.
- *
- * Returns: Description for an option.
- **/
- const char *core_option_manager_get_desc(
- core_option_manager_t *opt, size_t idx)
- {
- if (!opt)
- return NULL;
- if (idx >= opt->size)
- return NULL;
- return opt->opts[idx].desc;
- }
- /**
- * core_option_manager_get_info:
- * @opt : options manager handle
- * @idx : idx identifier of the option
- *
- * Gets information text for an option.
- *
- * Returns: Information text for an option.
- **/
- const char *core_option_manager_get_info(
- core_option_manager_t *opt, size_t idx)
- {
- if (!opt)
- return NULL;
- if (idx >= opt->size)
- return NULL;
- return opt->opts[idx].info;
- }
- /**
- * core_option_manager_get_val:
- * @opt : options manager handle
- * @index : index identifier of the option
- *
- * Gets value for an option.
- *
- * Returns: Value for an option.
- **/
- const char *core_option_manager_get_val(core_option_manager_t *opt, size_t idx)
- {
- struct core_option *option = NULL;
- if (!opt)
- return NULL;
- if (idx >= opt->size)
- return NULL;
- option = (struct core_option*)&opt->opts[idx];
- return option->vals->elems[option->index].data;
- }
- /**
- * core_option_manager_get_val_label:
- * @opt : options manager handle
- * @idx : idx identifier of the option
- *
- * Gets value label for an option.
- *
- * Returns: Value label for an option.
- **/
- const char *core_option_manager_get_val_label(core_option_manager_t *opt, size_t idx)
- {
- struct core_option *option = NULL;
- if (!opt)
- return NULL;
- if (idx >= opt->size)
- return NULL;
- option = (struct core_option*)&opt->opts[idx];
- return option->val_labels->elems[option->index].data;
- }
- /**
- * core_option_manager_get_visible:
- * @opt : options manager handle
- * @idx : idx identifier of the option
- *
- * Gets whether option should be visible when displaying
- * core options in the frontend
- *
- * Returns: 'true' if option should be displayed by the frontend.
- **/
- bool core_option_manager_get_visible(core_option_manager_t *opt,
- size_t idx)
- {
- if (!opt)
- return false;
- if (idx >= opt->size)
- return false;
- return opt->opts[idx].visible;
- }
- void core_option_manager_set_val(core_option_manager_t *opt,
- size_t idx, size_t val_idx)
- {
- struct core_option *option = NULL;
- if (!opt)
- return;
- if (idx >= opt->size)
- return;
- option = (struct core_option*)&opt->opts[idx];
- option->index = val_idx % option->vals->size;
- opt->updated = true;
- #ifdef HAVE_CHEEVOS
- rcheevos_validate_config_settings();
- #endif
- }
- static void core_option_manager_adjust_val(core_option_manager_t* opt,
- size_t idx, int adjustment)
- {
- struct core_option* option = NULL;
- if (!opt)
- return;
- if (idx >= opt->size)
- return;
- option = (struct core_option*)&opt->opts[idx];
- option->index = (option->index + option->vals->size + adjustment) % option->vals->size;
- opt->updated = true;
- #ifdef HAVE_CHEEVOS
- rcheevos_validate_config_settings();
- #endif
- }
- /**
- * core_option_manager_set_default:
- * @opt : pointer to core option manager object.
- * @idx : index of core option to be reset to defaults.
- *
- * Reset core option specified by @idx and sets default value for option.
- **/
- void core_option_manager_set_default(core_option_manager_t *opt, size_t idx)
- {
- if (!opt)
- return;
- if (idx >= opt->size)
- return;
- opt->opts[idx].index = opt->opts[idx].default_index;
- opt->updated = true;
- #ifdef HAVE_CHEEVOS
- rcheevos_validate_config_settings();
- #endif
- }
- static struct retro_core_option_definition *core_option_manager_get_definitions(
- const struct retro_core_options_intl *core_options_intl)
- {
- size_t i;
- size_t num_options = 0;
- struct retro_core_option_definition *option_defs_us = NULL;
- struct retro_core_option_definition *option_defs_local = NULL;
- struct retro_core_option_definition *option_defs = NULL;
- if (!core_options_intl)
- return NULL;
- option_defs_us = core_options_intl->us;
- option_defs_local = core_options_intl->local;
- if (!option_defs_us)
- return NULL;
- /* Determine number of options */
- for (;;)
- {
- if (string_is_empty(option_defs_us[num_options].key))
- break;
- num_options++;
- }
- if (num_options < 1)
- return NULL;
- /* Allocate output option_defs array
- * > One extra entry required for terminating NULL entry
- * > Note that calloc() sets terminating NULL entry and
- * correctly 'nullifies' each values array */
- option_defs = (struct retro_core_option_definition *)calloc(
- num_options + 1, sizeof(struct retro_core_option_definition));
- if (!option_defs)
- return NULL;
- /* Loop through options... */
- for (i = 0; i < num_options; i++)
- {
- size_t j;
- size_t num_values = 0;
- const char *key = option_defs_us[i].key;
- const char *local_desc = NULL;
- const char *local_info = NULL;
- struct retro_core_option_value *local_values = NULL;
- /* Key is always taken from us english defs */
- option_defs[i].key = key;
- /* Default value is always taken from us english defs */
- option_defs[i].default_value = option_defs_us[i].default_value;
- /* Try to find corresponding entry in local defs array */
- if (option_defs_local)
- {
- size_t index = 0;
- for (;;)
- {
- const char *local_key = option_defs_local[index].key;
- if (string_is_empty(local_key))
- break;
- if (string_is_equal(key, local_key))
- {
- local_desc = option_defs_local[index].desc;
- local_info = option_defs_local[index].info;
- local_values = option_defs_local[index].values;
- break;
- }
- index++;
- }
- }
- /* Set desc and info strings */
- option_defs[i].desc = string_is_empty(local_desc) ? option_defs_us[i].desc : local_desc;
- option_defs[i].info = string_is_empty(local_info) ? option_defs_us[i].info : local_info;
- /* Determine number of values
- * (always taken from us english defs) */
- for (;;)
- {
- if (string_is_empty(option_defs_us[i].values[num_values].value))
- break;
- num_values++;
- }
- /* Copy values */
- for (j = 0; j < num_values; j++)
- {
- const char *value = option_defs_us[i].values[j].value;
- const char *local_label = NULL;
- /* Value string is always taken from us english defs */
- option_defs[i].values[j].value = value;
- /* Try to find corresponding entry in local defs values array */
- if (local_values)
- {
- size_t value_index = 0;
- for (;;)
- {
- const char *local_value = local_values[value_index].value;
- if (string_is_empty(local_value))
- break;
- if (string_is_equal(value, local_value))
- {
- local_label = local_values[value_index].label;
- break;
- }
- value_index++;
- }
- }
- /* Set value label string */
- option_defs[i].values[j].label = string_is_empty(local_label) ?
- option_defs_us[i].values[j].label : local_label;
- }
- }
- return option_defs;
- }
- static void core_option_manager_set_display(core_option_manager_t *opt,
- const char *key, bool visible)
- {
- size_t i;
- if (!opt || string_is_empty(key))
- return;
- for (i = 0; i < opt->size; i++)
- {
- if (string_is_empty(opt->opts[i].key))
- continue;
- if (string_is_equal(opt->opts[i].key, key))
- {
- opt->opts[i].visible = visible;
- return;
- }
- }
- }
- /* DYNAMIC LIBRETRO CORE */
- const struct retro_subsystem_info *libretro_find_subsystem_info(
- const struct retro_subsystem_info *info, unsigned num_info,
- const char *ident)
- {
- unsigned i;
- for (i = 0; i < num_info; i++)
- {
- if (string_is_equal(info[i].ident, ident))
- return &info[i];
- else if (string_is_equal(info[i].desc, ident))
- return &info[i];
- }
- return NULL;
- }
- /**
- * libretro_find_controller_description:
- * @info : Pointer to controller info handle.
- * @id : Identifier of controller to search
- * for.
- *
- * Search for a controller of type @id in @info.
- *
- * Returns: controller description of found controller on success,
- * otherwise NULL.
- **/
- const struct retro_controller_description *
- libretro_find_controller_description(
- const struct retro_controller_info *info, unsigned id)
- {
- unsigned i;
- for (i = 0; i < info->num_types; i++)
- {
- if (info->types[i].id != id)
- continue;
- return &info->types[i];
- }
- return NULL;
- }
- /**
- * libretro_free_system_info:
- * @info : Pointer to system info information.
- *
- * Frees system information.
- **/
- void libretro_free_system_info(struct retro_system_info *info)
- {
- if (!info)
- return;
- free((void*)info->library_name);
- free((void*)info->library_version);
- free((void*)info->valid_extensions);
- memset(info, 0, sizeof(*info));
- }
- static bool environ_cb_get_system_info(unsigned cmd, void *data)
- {
- struct rarch_state *p_rarch = &rarch_st;
- rarch_system_info_t *system = &p_rarch->runloop_system;
- switch (cmd)
- {
- case RETRO_ENVIRONMENT_SET_SUPPORT_NO_GAME:
- *p_rarch->load_no_content_hook = *(const bool*)data;
- break;
- case RETRO_ENVIRONMENT_SET_SUBSYSTEM_INFO:
- {
- unsigned i, j, size;
- const struct retro_subsystem_info *info =
- (const struct retro_subsystem_info*)data;
- settings_t *settings = p_rarch->configuration_settings;
- unsigned log_level = settings->uints.libretro_log_level;
- subsystem_current_count = 0;
- RARCH_LOG("[Environ]: SET_SUBSYSTEM_INFO.\n");
- for (i = 0; info[i].ident; i++)
- {
- if (log_level != RETRO_LOG_DEBUG)
- continue;
- RARCH_LOG("Subsystem ID: %d\nSpecial game type: %s\n Ident: %s\n ID: %u\n Content:\n",
- i,
- info[i].desc,
- info[i].ident,
- info[i].id
- );
- for (j = 0; j < info[i].num_roms; j++)
- {
- RARCH_LOG(" %s (%s)\n",
- info[i].roms[j].desc, info[i].roms[j].required ?
- "required" : "optional");
- }
- }
- size = i;
- if (log_level == RETRO_LOG_DEBUG)
- {
- RARCH_LOG("Subsystems: %d\n", i);
- if (size > SUBSYSTEM_MAX_SUBSYSTEMS)
- RARCH_WARN("Subsystems exceed subsystem max, clamping to %d\n", SUBSYSTEM_MAX_SUBSYSTEMS);
- }
- if (system)
- {
- for (i = 0; i < size && i < SUBSYSTEM_MAX_SUBSYSTEMS; i++)
- {
- /* Nasty, but have to do it like this since
- * the pointers are const char *
- * (if we don't free them, we get a memory leak) */
- if (!string_is_empty(subsystem_data[i].desc))
- free((char *)subsystem_data[i].desc);
- if (!string_is_empty(subsystem_data[i].ident))
- free((char *)subsystem_data[i].ident);
- subsystem_data[i].desc = strdup(info[i].desc);
- subsystem_data[i].ident = strdup(info[i].ident);
- subsystem_data[i].id = info[i].id;
- subsystem_data[i].num_roms = info[i].num_roms;
- if (log_level == RETRO_LOG_DEBUG)
- if (subsystem_data[i].num_roms > SUBSYSTEM_MAX_SUBSYSTEM_ROMS)
- RARCH_WARN("Subsystems exceed subsystem max roms, clamping to %d\n", SUBSYSTEM_MAX_SUBSYSTEM_ROMS);
- for (j = 0; j < subsystem_data[i].num_roms && j < SUBSYSTEM_MAX_SUBSYSTEM_ROMS; j++)
- {
- /* Nasty, but have to do it like this since
- * the pointers are const char *
- * (if we don't free them, we get a memory leak) */
- if (!string_is_empty(p_rarch->subsystem_data_roms[i][j].desc))
- free((char *)p_rarch->subsystem_data_roms[i][j].desc);
- if (!string_is_empty(p_rarch->subsystem_data_roms[i][j].valid_extensions))
- free((char *)p_rarch->subsystem_data_roms[i][j].valid_extensions);
- p_rarch->subsystem_data_roms[i][j].desc = strdup(info[i].roms[j].desc);
- p_rarch->subsystem_data_roms[i][j].valid_extensions = strdup(info[i].roms[j].valid_extensions);
- p_rarch->subsystem_data_roms[i][j].required = info[i].roms[j].required;
- p_rarch->subsystem_data_roms[i][j].block_extract = info[i].roms[j].block_extract;
- p_rarch->subsystem_data_roms[i][j].need_fullpath = info[i].roms[j].need_fullpath;
- }
- subsystem_data[i].roms = p_rarch->subsystem_data_roms[i];
- }
- subsystem_current_count =
- size <= SUBSYSTEM_MAX_SUBSYSTEMS
- ? size
- : SUBSYSTEM_MAX_SUBSYSTEMS;
- }
- break;
- }
- default:
- return false;
- }
- return true;
- }
- static bool dynamic_request_hw_context(enum retro_hw_context_type type,
- unsigned minor, unsigned major)
- {
- switch (type)
- {
- case RETRO_HW_CONTEXT_NONE:
- RARCH_LOG("Requesting no HW context.\n");
- break;
- case RETRO_HW_CONTEXT_VULKAN:
- #ifdef HAVE_VULKAN
- RARCH_LOG("Requesting Vulkan context.\n");
- break;
- #else
- RARCH_ERR("Requesting Vulkan context, but RetroArch is not compiled against Vulkan. Cannot use HW context.\n");
- return false;
- #endif
- #if defined(HAVE_OPENGLES)
- #if (defined(HAVE_OPENGLES2) || defined(HAVE_OPENGLES3))
- case RETRO_HW_CONTEXT_OPENGLES2:
- case RETRO_HW_CONTEXT_OPENGLES3:
- RARCH_LOG("Requesting OpenGLES%u context.\n",
- type == RETRO_HW_CONTEXT_OPENGLES2 ? 2 : 3);
- break;
- #if defined(HAVE_OPENGLES3)
- case RETRO_HW_CONTEXT_OPENGLES_VERSION:
- #ifndef HAVE_OPENGLES3_2
- if (major == 3 && minor == 2)
- {
- RARCH_ERR("Requesting OpenGLES%u.%u context, but RetroArch is compiled against a lesser version. Cannot use HW context.\n",
- major, minor);
- return false;
- }
- #endif
- #if !defined(HAVE_OPENGLES3_2) && !defined(HAVE_OPENGLES3_1)
- if (major == 3 && minor == 1)
- {
- RARCH_ERR("Requesting OpenGLES%u.%u context, but RetroArch is compiled against a lesser version. Cannot use HW context.\n",
- major, minor);
- return false;
- }
- #endif
- RARCH_LOG("Requesting OpenGLES%u.%u context.\n",
- major, minor);
- break;
- #endif
- #endif
- case RETRO_HW_CONTEXT_OPENGL:
- case RETRO_HW_CONTEXT_OPENGL_CORE:
- RARCH_ERR("Requesting OpenGL context, but RetroArch "
- "is compiled against OpenGLES. Cannot use HW context.\n");
- return false;
- #elif defined(HAVE_OPENGL) || defined(HAVE_OPENGL_CORE)
- case RETRO_HW_CONTEXT_OPENGLES2:
- case RETRO_HW_CONTEXT_OPENGLES3:
- RARCH_ERR("Requesting OpenGLES%u context, but RetroArch "
- "is compiled against OpenGL. Cannot use HW context.\n",
- type == RETRO_HW_CONTEXT_OPENGLES2 ? 2 : 3);
- return false;
- case RETRO_HW_CONTEXT_OPENGLES_VERSION:
- RARCH_ERR("Requesting OpenGLES%u.%u context, but RetroArch "
- "is compiled against OpenGL. Cannot use HW context.\n",
- major, minor);
- return false;
- case RETRO_HW_CONTEXT_OPENGL:
- RARCH_LOG("Requesting OpenGL context.\n");
- break;
- case RETRO_HW_CONTEXT_OPENGL_CORE:
- /* TODO/FIXME - we should do a check here to see if
- * the requested core GL version is supported */
- RARCH_LOG("Requesting core OpenGL context (%u.%u).\n",
- major, minor);
- break;
- #endif
- #if defined(HAVE_D3D9) || defined(HAVE_D3D11)
- case RETRO_HW_CONTEXT_DIRECT3D:
- switch (major)
- {
- #ifdef HAVE_D3D9
- case 9:
- RARCH_LOG("Requesting D3D9 context.\n");
- break;
- #endif
- #ifdef HAVE_D3D11
- case 11:
- RARCH_LOG("Requesting D3D11 context.\n");
- break;
- #endif
- default:
- RARCH_LOG("Requesting unknown context.\n");
- return false;
- }
- break;
- #endif
- default:
- RARCH_LOG("Requesting unknown context.\n");
- return false;
- }
- return true;
- }
- static bool dynamic_verify_hw_context(
- settings_t *settings,
- enum retro_hw_context_type type,
- unsigned minor, unsigned major)
- {
- const char *video_ident = settings->arrays.video_driver;
- bool driver_switch_enable = settings->bools.driver_switch_enable;
- if (driver_switch_enable)
- return true;
- switch (type)
- {
- case RETRO_HW_CONTEXT_VULKAN:
- if (!string_is_equal(video_ident, "vulkan"))
- return false;
- break;
- #if defined(HAVE_OPENGL_CORE)
- case RETRO_HW_CONTEXT_OPENGL_CORE:
- if (!string_is_equal(video_ident, "glcore"))
- return false;
- break;
- #else
- case RETRO_HW_CONTEXT_OPENGL_CORE:
- #endif
- case RETRO_HW_CONTEXT_OPENGLES2:
- case RETRO_HW_CONTEXT_OPENGLES3:
- case RETRO_HW_CONTEXT_OPENGLES_VERSION:
- case RETRO_HW_CONTEXT_OPENGL:
- if (!string_is_equal(video_ident, "gl") &&
- !string_is_equal(video_ident, "glcore"))
- return false;
- break;
- case RETRO_HW_CONTEXT_DIRECT3D:
- if (!(string_is_equal(video_ident, "d3d11") && major == 11))
- return false;
- break;
- default:
- break;
- }
- return true;
- }
- static void rarch_log_libretro(
- enum retro_log_level level,
- const char *fmt, ...)
- {
- va_list vp;
- struct rarch_state *p_rarch = &rarch_st;
- settings_t *settings = p_rarch->configuration_settings;
- unsigned libretro_log_level = settings->uints.libretro_log_level;
- if ((unsigned)level < libretro_log_level)
- return;
- if (!verbosity_is_enabled())
- return;
- va_start(vp, fmt);
- switch (level)
- {
- case RETRO_LOG_DEBUG:
- RARCH_LOG_V("[libretro DEBUG]", fmt, vp);
- break;
- case RETRO_LOG_INFO:
- RARCH_LOG_OUTPUT_V("[libretro INFO]", fmt, vp);
- break;
- case RETRO_LOG_WARN:
- RARCH_WARN_V("[libretro WARN]", fmt, vp);
- break;
- case RETRO_LOG_ERROR:
- RARCH_ERR_V("[libretro ERROR]", fmt, vp);
- break;
- default:
- break;
- }
- va_end(vp);
- }
- static void core_performance_counter_start(
- struct retro_perf_counter *perf)
- {
- struct rarch_state *p_rarch = &rarch_st;
- bool runloop_perfcnt_enable = p_rarch->runloop_perfcnt_enable;
- if (runloop_perfcnt_enable)
- {
- perf->call_cnt++;
- perf->start = cpu_features_get_perf_counter();
- }
- }
- static void core_performance_counter_stop(struct retro_perf_counter *perf)
- {
- struct rarch_state *p_rarch = &rarch_st;
- bool runloop_perfcnt_enable = p_rarch->runloop_perfcnt_enable;
- if (runloop_perfcnt_enable)
- perf->total += cpu_features_get_perf_counter() - perf->start;
- }
- static size_t mmap_add_bits_down(size_t n)
- {
- n |= n >> 1;
- n |= n >> 2;
- n |= n >> 4;
- n |= n >> 8;
- n |= n >> 16;
- /* double shift to avoid warnings on 32bit (it's dead code,
- * but compilers suck) */
- if (sizeof(size_t) > 4)
- n |= n >> 16 >> 16;
- return n;
- }
- static size_t mmap_inflate(size_t addr, size_t mask)
- {
- while (mask)
- {
- size_t tmp = (mask - 1) & ~mask;
- /* to put in an 1 bit instead, OR in tmp+1 */
- addr = ((addr & ~tmp) << 1) | (addr & tmp);
- mask = mask & (mask - 1);
- }
- return addr;
- }
- static size_t mmap_reduce(size_t addr, size_t mask)
- {
- while (mask)
- {
- size_t tmp = (mask - 1) & ~mask;
- addr = (addr & tmp) | ((addr >> 1) & ~tmp);
- mask = (mask & (mask - 1)) >> 1;
- }
- return addr;
- }
- static size_t mmap_highest_bit(size_t n)
- {
- n = mmap_add_bits_down(n);
- return n ^ (n >> 1);
- }
- static bool mmap_preprocess_descriptors(
- rarch_memory_descriptor_t *first, unsigned count)
- {
- size_t top_addr = 1;
- rarch_memory_descriptor_t *desc = NULL;
- const rarch_memory_descriptor_t *end = first + count;
- for (desc = first; desc < end; desc++)
- {
- if (desc->core.select != 0)
- top_addr |= desc->core.select;
- else
- top_addr |= desc->core.start + desc->core.len - 1;
- }
- top_addr = mmap_add_bits_down(top_addr);
- for (desc = first; desc < end; desc++)
- {
- if (desc->core.select == 0)
- {
- if (desc->core.len == 0)
- return false;
- if ((desc->core.len & (desc->core.len - 1)) != 0)
- return false;
- desc->core.select = top_addr & ~mmap_inflate(mmap_add_bits_down(desc->core.len - 1),
- desc->core.disconnect);
- }
- if (desc->core.len == 0)
- desc->core.len = mmap_add_bits_down(mmap_reduce(top_addr & ~desc->core.select,
- desc->core.disconnect)) + 1;
- if (desc->core.start & ~desc->core.select)
- return false;
- while (mmap_reduce(top_addr & ~desc->core.select, desc->core.disconnect) >> 1 > desc->core.len - 1)
- desc->core.disconnect |= mmap_highest_bit(top_addr & ~desc->core.select & ~desc->core.disconnect);
- desc->disconnect_mask = mmap_add_bits_down(desc->core.len - 1);
- desc->core.disconnect &= desc->disconnect_mask;
- while ((~desc->disconnect_mask) >> 1 & desc->core.disconnect)
- {
- desc->disconnect_mask >>= 1;
- desc->core.disconnect &= desc->disconnect_mask;
- }
- }
- return true;
- }
- static bool rarch_clear_all_thread_waits(
- unsigned clear_threads, void *data)
- {
- struct rarch_state *p_rarch = &rarch_st;
- if ( clear_threads > 0)
- audio_driver_start(p_rarch,
- false);
- else
- audio_driver_stop(p_rarch);
- return true;
- }
- static void runloop_core_msg_queue_push(
- struct retro_system_av_info *av_info,
- const struct retro_message_ext *msg)
- {
- double fps;
- unsigned duration_frames;
- enum message_queue_category category;
- /* Assign category */
- switch (msg->level)
- {
- case RETRO_LOG_WARN:
- category = MESSAGE_QUEUE_CATEGORY_WARNING;
- break;
- case RETRO_LOG_ERROR:
- category = MESSAGE_QUEUE_CATEGORY_ERROR;
- break;
- case RETRO_LOG_INFO:
- case RETRO_LOG_DEBUG:
- default:
- category = MESSAGE_QUEUE_CATEGORY_INFO;
- break;
- }
- /* Get duration in frames */
- fps = (av_info && (av_info->timing.fps > 0)) ? av_info->timing.fps : 60.0;
- duration_frames = (unsigned)((fps * (float)msg->duration / 1000.0f) + 0.5f);
- /* Note: Do not flush the message queue here - a core
- * may need to send multiple notifications simultaneously */
- runloop_msg_queue_push(msg->msg,
- msg->priority, duration_frames,
- false, NULL, MESSAGE_QUEUE_ICON_DEFAULT,
- category);
- }
- #ifdef HAVE_VULKAN
- static bool hw_render_context_is_vulkan(enum retro_hw_context_type type)
- {
- return type == RETRO_HW_CONTEXT_VULKAN;
- }
- #endif
- #ifdef HAVE_D3D9
- static bool hw_render_context_is_d3d9(enum retro_hw_context_type type, int major, int minor)
- {
- return type == RETRO_HW_CONTEXT_DIRECT3D && major == 9;
- }
- #endif
- #ifdef HAVE_D3D11
- static bool hw_render_context_is_d3d11(enum retro_hw_context_type type, int major, int minor)
- {
- return type == RETRO_HW_CONTEXT_DIRECT3D && major == 11;
- }
- #endif
- #ifdef HAVE_OPENGL
- static bool hw_render_context_is_gl(enum retro_hw_context_type type)
- {
- switch (type)
- {
- case RETRO_HW_CONTEXT_OPENGLES2:
- case RETRO_HW_CONTEXT_OPENGLES3:
- case RETRO_HW_CONTEXT_OPENGLES_VERSION:
- case RETRO_HW_CONTEXT_OPENGL:
- #ifndef HAVE_OPENGL_CORE
- case RETRO_HW_CONTEXT_OPENGL_CORE:
- #endif
- return true;
- default:
- break;
- }
- return false;
- }
- #endif
- #ifdef HAVE_OPENGL_CORE
- static bool hw_render_context_is_glcore(enum retro_hw_context_type type)
- {
- return type == RETRO_HW_CONTEXT_OPENGL_CORE;
- }
- #endif
- #if defined(HAVE_VULKAN) || defined(HAVE_D3D11) || defined(HAVE_D3D9) || defined(HAVE_OPENGL_CORE)
- static video_driver_t *hw_render_context_driver(enum retro_hw_context_type type, int major, int minor)
- {
- switch (type)
- {
- case RETRO_HW_CONTEXT_OPENGL_CORE:
- #ifdef HAVE_OPENGL_CORE
- return &video_gl_core;
- #else
- break;
- #endif
- case RETRO_HW_CONTEXT_OPENGL:
- #ifdef HAVE_OPENGL
- return &video_gl2;
- #else
- break;
- #endif
- case RETRO_HW_CONTEXT_DIRECT3D:
- #if defined(HAVE_D3D9)
- if (major == 9)
- return &video_d3d9;
- #endif
- #if defined(HAVE_D3D11)
- if (major == 11)
- return &video_d3d11;
- #endif
- break;
- case RETRO_HW_CONTEXT_VULKAN:
- #if defined(HAVE_VULKAN)
- return &video_vulkan;
- #else
- break;
- #endif
- default:
- case RETRO_HW_CONTEXT_NONE:
- break;
- }
- return NULL;
- }
- #endif
- static enum retro_hw_context_type hw_render_context_type(const char *s)
- {
- #ifdef HAVE_OPENGL_CORE
- if (string_is_equal(s, "glcore"))
- return RETRO_HW_CONTEXT_OPENGL_CORE;
- #endif
- #ifdef HAVE_OPENGL
- if (string_is_equal(s, "gl"))
- return RETRO_HW_CONTEXT_OPENGL;
- #endif
- #ifdef HAVE_VULKAN
- if (string_is_equal(s, "vulkan"))
- return RETRO_HW_CONTEXT_VULKAN;
- #endif
- #ifdef HAVE_D3D11
- if (string_is_equal(s, "d3d11"))
- return RETRO_HW_CONTEXT_DIRECT3D;
- #endif
- #ifdef HAVE_D3D11
- if (string_is_equal(s, "d3d9"))
- return RETRO_HW_CONTEXT_DIRECT3D;
- #endif
- return RETRO_HW_CONTEXT_NONE;
- }
- static const char *hw_render_context_name(enum retro_hw_context_type type, int major, int minor)
- {
- #ifdef HAVE_OPENGL_CORE
- if (hw_render_context_is_glcore(type))
- return "glcore";
- #endif
- #ifdef HAVE_OPENGL
- if (hw_render_context_is_gl(type))
- return "gl";
- #endif
- #ifdef HAVE_VULKAN
- if (hw_render_context_is_vulkan(type))
- return "vulkan";
- #endif
- #ifdef HAVE_D3D11
- if (hw_render_context_is_d3d11(type, major, minor))
- return "d3d11";
- #endif
- #ifdef HAVE_D3D9
- if (hw_render_context_is_d3d9(type, major, minor))
- return "d3d9";
- #endif
- return "N/A";
- }
- /**
- * rarch_environment_cb:
- * @cmd : Identifier of command.
- * @data : Pointer to data.
- *
- * Environment callback function implementation.
- *
- * Returns: true (1) if environment callback command could
- * be performed, otherwise false (0).
- **/
- static bool rarch_environment_cb(unsigned cmd, void *data)
- {
- unsigned p;
- struct rarch_state *p_rarch = &rarch_st;
- settings_t *settings = p_rarch->configuration_settings;
- rarch_system_info_t *system = &p_rarch->runloop_system;
- bool ignore_environment_cb = p_rarch->ignore_environment_cb;
- if (ignore_environment_cb)
- return false;
- switch (cmd)
- {
- case RETRO_ENVIRONMENT_GET_OVERSCAN:
- {
- bool video_crop_overscan = settings->bools.video_crop_overscan;
- *(bool*)data = !video_crop_overscan;
- RARCH_LOG("[Environ]: GET_OVERSCAN: %u\n",
- (unsigned)!video_crop_overscan);
- }
- break;
- case RETRO_ENVIRONMENT_GET_CAN_DUPE:
- *(bool*)data = true;
- RARCH_LOG("[Environ]: GET_CAN_DUPE: true\n");
- break;
- case RETRO_ENVIRONMENT_GET_VARIABLE:
- {
- unsigned log_level = settings->uints.libretro_log_level;
- struct retro_variable *var = (struct retro_variable*)data;
- if (!var)
- return true;
- var->value = NULL;
- if (!p_rarch->runloop_core_options)
- {
- RARCH_LOG("[Environ]: GET_VARIABLE %s: not implemented.\n",
- var->key);
- return true;
- }
- {
- size_t i;
- #ifdef HAVE_RUNAHEAD
- if (p_rarch->runloop_core_options->updated)
- p_rarch->has_variable_update = true;
- #endif
- p_rarch->runloop_core_options->updated = false;
- for (i = 0; i < p_rarch->runloop_core_options->size; i++)
- {
- if (!string_is_empty(p_rarch->runloop_core_options->opts[i].key))
- if (string_is_equal(
- p_rarch->runloop_core_options->opts[i].key, var->key))
- {
- var->value = p_rarch->runloop_core_options->opts[i].vals->elems[
- p_rarch->runloop_core_options->opts[i].index].data;
- break;
- }
- }
- }
- if (log_level == RETRO_LOG_DEBUG)
- {
- char s[128];
- s[0] = '\0';
- snprintf(s, sizeof(s), "[Environ]: GET_VARIABLE %s:\n\t%s\n", var->key, var->value ? var->value :
- msg_hash_to_str(MENU_ENUM_LABEL_VALUE_NOT_AVAILABLE));
- RARCH_LOG(s);
- }
- }
- break;
- case RETRO_ENVIRONMENT_GET_VARIABLE_UPDATE:
- if (p_rarch->runloop_core_options)
- *(bool*)data = p_rarch->runloop_core_options->updated;
- else
- *(bool*)data = false;
- break;
- /* SET_VARIABLES: Legacy path */
- case RETRO_ENVIRONMENT_SET_VARIABLES:
- RARCH_LOG("[Environ]: SET_VARIABLES.\n");
- if (p_rarch->runloop_core_options)
- retroarch_deinit_core_options(p_rarch);
- retroarch_init_core_variables(
- p_rarch,
- (const struct retro_variable *)data);
- break;
- case RETRO_ENVIRONMENT_SET_CORE_OPTIONS:
- RARCH_LOG("[Environ]: SET_CORE_OPTIONS.\n");
- if (p_rarch->runloop_core_options)
- retroarch_deinit_core_options(p_rarch);
- rarch_init_core_options(p_rarch,
- (const struct retro_core_option_definition*)data);
- break;
- case RETRO_ENVIRONMENT_SET_CORE_OPTIONS_INTL:
- RARCH_LOG("[Environ]: RETRO_ENVIRONMENT_SET_CORE_OPTIONS_INTL.\n");
- {
- struct retro_core_option_definition *option_defs =
- core_option_manager_get_definitions((const struct retro_core_options_intl*)data);
- if (p_rarch->runloop_core_options)
- retroarch_deinit_core_options(p_rarch);
- /* Parse core_options_intl to create option definitions array */
- if (option_defs)
- {
- /* Initialise core options */
- rarch_init_core_options(p_rarch, option_defs);
- /* Clean up */
- free(option_defs);
- }
- }
- break;
- case RETRO_ENVIRONMENT_SET_CORE_OPTIONS_DISPLAY:
- RARCH_DBG("[Environ]: RETRO_ENVIRONMENT_SET_CORE_OPTIONS_DISPLAY.\n");
- {
- const struct retro_core_option_display *core_options_display = (const struct retro_core_option_display *)data;
- if (p_rarch->runloop_core_options && core_options_display)
- core_option_manager_set_display(
- p_rarch->runloop_core_options,
- core_options_display->key,
- core_options_display->visible);
- }
- break;
- case RETRO_ENVIRONMENT_GET_MESSAGE_INTERFACE_VERSION:
- RARCH_LOG("[Environ]: GET_MESSAGE_INTERFACE_VERSION.\n");
- /* Current API version is 1 */
- *(unsigned *)data = 1;
- break;
- case RETRO_ENVIRONMENT_SET_MESSAGE:
- {
- const struct retro_message *msg = (const struct retro_message*)data;
- RARCH_LOG("[Environ]: SET_MESSAGE: %s\n", msg->msg);
- #if defined(HAVE_GFX_WIDGETS)
- if (p_rarch->widgets_active)
- gfx_widget_set_libretro_message(&p_rarch->dispwidget_st,
- msg->msg,
- roundf((float)msg->frames / 60.0f * 1000.0f));
- else
- #endif
- runloop_msg_queue_push(msg->msg, 3, msg->frames,
- true, NULL, MESSAGE_QUEUE_ICON_DEFAULT,
- MESSAGE_QUEUE_CATEGORY_INFO);
- break;
- }
- case RETRO_ENVIRONMENT_SET_MESSAGE_EXT:
- {
- const struct retro_message_ext *msg =
- (const struct retro_message_ext*)data;
- /* Log message, if required */
- if (msg->target != RETRO_MESSAGE_TARGET_OSD)
- {
- settings_t *settings = p_rarch->configuration_settings;
- unsigned log_level = settings->uints.frontend_log_level;
- switch (msg->level)
- {
- case RETRO_LOG_DEBUG:
- if (log_level == RETRO_LOG_DEBUG)
- RARCH_LOG("[Environ]: SET_MESSAGE_EXT: %s\n", msg->msg);
- break;
- case RETRO_LOG_WARN:
- RARCH_WARN("[Environ]: SET_MESSAGE_EXT: %s\n", msg->msg);
- break;
- case RETRO_LOG_ERROR:
- RARCH_ERR("[Environ]: SET_MESSAGE_EXT: %s\n", msg->msg);
- break;
- case RETRO_LOG_INFO:
- default:
- RARCH_LOG("[Environ]: SET_MESSAGE_EXT: %s\n", msg->msg);
- break;
- }
- }
- /* Display message via OSD, if required */
- if (msg->target != RETRO_MESSAGE_TARGET_LOG)
- {
- switch (msg->type)
- {
- /* Handle 'status' messages */
- case RETRO_MESSAGE_TYPE_STATUS:
- /* Note: We need to lock a mutex here. Strictly
- * speaking, runloop_core_status_msg is not part
- * of the message queue, but:
- * - It may be implemented as a queue in the future
- * - It seems unnecessary to create a new slock_t
- * object for this type of message when
- * _runloop_msg_queue_lock is already available
- * We therefore just call runloop_msg_queue_lock()/
- * runloop_msg_queue_unlock() in this case */
- RUNLOOP_MSG_QUEUE_LOCK(p_rarch);
- /* If a message is already set, only overwrite
- * it if the new message has the same or higher
- * priority */
- if (!runloop_core_status_msg.set ||
- (runloop_core_status_msg.priority <= msg->priority))
- {
- if (!string_is_empty(msg->msg))
- {
- strlcpy(runloop_core_status_msg.str, msg->msg,
- sizeof(runloop_core_status_msg.str));
- runloop_core_status_msg.duration = (float)msg->duration;
- runloop_core_status_msg.set = true;
- }
- else
- {
- /* Ensure sane behaviour if core sends an
- * empty message */
- runloop_core_status_msg.str[0] = '\0';
- runloop_core_status_msg.priority = 0;
- runloop_core_status_msg.duration = 0.0f;
- runloop_core_status_msg.set = false;
- }
- }
- RUNLOOP_MSG_QUEUE_UNLOCK(p_rarch);
- break;
- #if defined(HAVE_GFX_WIDGETS)
- /* Handle 'alternate' non-queued notifications */
- case RETRO_MESSAGE_TYPE_NOTIFICATION_ALT:
- if (p_rarch->widgets_active)
- gfx_widget_set_libretro_message(&p_rarch->dispwidget_st,
- msg->msg, msg->duration);
- else
- runloop_core_msg_queue_push(
- &p_rarch->video_driver_av_info, msg);
- break;
- /* Handle 'progress' messages */
- case RETRO_MESSAGE_TYPE_PROGRESS:
- if (p_rarch->widgets_active)
- gfx_widget_set_progress_message(&p_rarch->dispwidget_st,
- msg->msg, msg->duration,
- msg->priority, msg->progress);
- else
- runloop_core_msg_queue_push(
- &p_rarch->video_driver_av_info, msg);
- break;
- #endif
- /* Handle standard (queued) notifications */
- case RETRO_MESSAGE_TYPE_NOTIFICATION:
- default:
- runloop_core_msg_queue_push(
- &p_rarch->video_driver_av_info, msg);
- break;
- }
- }
- break;
- }
- case RETRO_ENVIRONMENT_SET_ROTATION:
- {
- unsigned rotation = *(const unsigned*)data;
- bool video_allow_rotate = settings->bools.video_allow_rotate;
- RARCH_LOG("[Environ]: SET_ROTATION: %u\n", rotation);
- if (!video_allow_rotate)
- break;
- if (system)
- system->rotation = rotation;
- if (!video_driver_set_rotation(rotation))
- return false;
- break;
- }
- case RETRO_ENVIRONMENT_SHUTDOWN:
- RARCH_LOG("[Environ]: SHUTDOWN.\n");
- /* This case occurs when a core (internally) requests
- * a shutdown event. Must save runtime log file here,
- * since normal command.c CMD_EVENT_CORE_DEINIT event
- * will not occur until after the current content has
- * been cleared (causing log to be skipped) */
- command_event_runtime_log_deinit(p_rarch);
- p_rarch->runloop_shutdown_initiated = true;
- p_rarch->runloop_core_shutdown_initiated = true;
- break;
- case RETRO_ENVIRONMENT_SET_PERFORMANCE_LEVEL:
- if (system)
- {
- system->performance_level = *(const unsigned*)data;
- RARCH_LOG("[Environ]: PERFORMANCE_LEVEL: %u.\n",
- system->performance_level);
- }
- break;
- case RETRO_ENVIRONMENT_GET_SYSTEM_DIRECTORY:
- {
- const char *dir_system = settings->paths.directory_system;
- bool systemfiles_in_content_dir = settings->bools.systemfiles_in_content_dir;
- if (string_is_empty(dir_system) || systemfiles_in_content_dir)
- {
- const char *fullpath = path_get(RARCH_PATH_CONTENT);
- if (!string_is_empty(fullpath))
- {
- char temp_path[PATH_MAX_LENGTH];
- temp_path[0] = '\0';
- if (string_is_empty(dir_system))
- RARCH_WARN("[Environ]: SYSTEM DIR is empty, assume CONTENT DIR %s\n",
- fullpath);
- fill_pathname_basedir(temp_path, fullpath, sizeof(temp_path));
- dir_set(RARCH_DIR_SYSTEM, temp_path);
- }
- *(const char**)data = dir_get_ptr(RARCH_DIR_SYSTEM);
- RARCH_LOG("[Environ]: SYSTEM_DIRECTORY: \"%s\".\n",
- dir_system);
- }
- else
- {
- *(const char**)data = dir_system;
- RARCH_LOG("[Environ]: SYSTEM_DIRECTORY: \"%s\".\n",
- dir_system);
- }
- }
- break;
- case RETRO_ENVIRONMENT_GET_SAVE_DIRECTORY:
- RARCH_LOG("[Environ]: GET_SAVE_DIRECTORY.\n");
- *(const char**)data = p_rarch->current_savefile_dir;
- break;
- case RETRO_ENVIRONMENT_GET_USERNAME:
- *(const char**)data = *settings->paths.username ?
- settings->paths.username : NULL;
- RARCH_LOG("[Environ]: GET_USERNAME: \"%s\".\n",
- settings->paths.username);
- break;
- case RETRO_ENVIRONMENT_GET_LANGUAGE:
- #ifdef HAVE_LANGEXTRA
- {
- unsigned user_lang = *msg_hash_get_uint(MSG_HASH_USER_LANGUAGE);
- *(unsigned *)data = user_lang;
- RARCH_LOG("[Environ]: GET_LANGUAGE: \"%u\".\n", user_lang);
- }
- #endif
- break;
- case RETRO_ENVIRONMENT_SET_PIXEL_FORMAT:
- {
- enum retro_pixel_format pix_fmt =
- *(const enum retro_pixel_format*)data;
- switch (pix_fmt)
- {
- case RETRO_PIXEL_FORMAT_0RGB1555:
- RARCH_LOG("[Environ]: SET_PIXEL_FORMAT: 0RGB1555.\n");
- break;
- case RETRO_PIXEL_FORMAT_RGB565:
- RARCH_LOG("[Environ]: SET_PIXEL_FORMAT: RGB565.\n");
- break;
- case RETRO_PIXEL_FORMAT_XRGB8888:
- RARCH_LOG("[Environ]: SET_PIXEL_FORMAT: XRGB8888.\n");
- break;
- default:
- return false;
- }
- p_rarch->video_driver_pix_fmt = pix_fmt;
- break;
- }
- case RETRO_ENVIRONMENT_SET_INPUT_DESCRIPTORS:
- {
- static const char *libretro_btn_desc[] = {
- "B (bottom)", "Y (left)", "Select", "Start",
- "D-Pad Up", "D-Pad Down", "D-Pad Left", "D-Pad Right",
- "A (right)", "X (up)",
- "L", "R", "L2", "R2", "L3", "R3",
- };
- if (system)
- {
- unsigned retro_id;
- const struct retro_input_descriptor *desc = NULL;
- memset((void*)&system->input_desc_btn, 0,
- sizeof(system->input_desc_btn));
- desc = (const struct retro_input_descriptor*)data;
- for (; desc->description; desc++)
- {
- unsigned retro_port = desc->port;
- retro_id = desc->id;
- if (desc->port >= MAX_USERS)
- continue;
- if (desc->id >= RARCH_FIRST_CUSTOM_BIND)
- continue;
- switch (desc->device)
- {
- case RETRO_DEVICE_JOYPAD:
- system->input_desc_btn[retro_port]
- [retro_id] = desc->description;
- break;
- case RETRO_DEVICE_ANALOG:
- switch (retro_id)
- {
- case RETRO_DEVICE_ID_ANALOG_X:
- switch (desc->index)
- {
- case RETRO_DEVICE_INDEX_ANALOG_LEFT:
- system->input_desc_btn[retro_port]
- [RARCH_ANALOG_LEFT_X_PLUS] = desc->description;
- system->input_desc_btn[retro_port]
- [RARCH_ANALOG_LEFT_X_MINUS] = desc->description;
- break;
- case RETRO_DEVICE_INDEX_ANALOG_RIGHT:
- system->input_desc_btn[retro_port]
- [RARCH_ANALOG_RIGHT_X_PLUS] = desc->description;
- system->input_desc_btn[retro_port]
- [RARCH_ANALOG_RIGHT_X_MINUS] = desc->description;
- break;
- }
- break;
- case RETRO_DEVICE_ID_ANALOG_Y:
- switch (desc->index)
- {
- case RETRO_DEVICE_INDEX_ANALOG_LEFT:
- system->input_desc_btn[retro_port]
- [RARCH_ANALOG_LEFT_Y_PLUS] = desc->description;
- system->input_desc_btn[retro_port]
- [RARCH_ANALOG_LEFT_Y_MINUS] = desc->description;
- break;
- case RETRO_DEVICE_INDEX_ANALOG_RIGHT:
- system->input_desc_btn[retro_port]
- [RARCH_ANALOG_RIGHT_Y_PLUS] = desc->description;
- system->input_desc_btn[retro_port]
- [RARCH_ANALOG_RIGHT_Y_MINUS] = desc->description;
- break;
- }
- break;
- }
- break;
- }
- }
- RARCH_LOG("[Environ]: SET_INPUT_DESCRIPTORS:\n");
- {
- unsigned log_level = settings->uints.libretro_log_level;
- if (log_level == RETRO_LOG_DEBUG)
- {
- unsigned input_driver_max_users =
- p_rarch->input_driver_max_users;
- for (p = 0; p < input_driver_max_users; p++)
- {
- for (retro_id = 0; retro_id < RARCH_FIRST_CUSTOM_BIND; retro_id++)
- {
- const char *description = system->input_desc_btn[p][retro_id];
- if (!description)
- continue;
- RARCH_LOG("\tRetroPad, Port %u, Button \"%s\" => \"%s\"\n",
- p + 1, libretro_btn_desc[retro_id], description);
- }
- }
- }
- }
- p_rarch->current_core.has_set_input_descriptors = true;
- }
- break;
- }
- case RETRO_ENVIRONMENT_SET_KEYBOARD_CALLBACK:
- {
- const struct retro_keyboard_callback *info =
- (const struct retro_keyboard_callback*)data;
- retro_keyboard_event_t *frontend_key_event = &p_rarch->runloop_frontend_key_event;
- retro_keyboard_event_t *key_event = &p_rarch->runloop_key_event;
- RARCH_LOG("[Environ]: SET_KEYBOARD_CALLBACK.\n");
- if (key_event)
- *key_event = info->callback;
- if (frontend_key_event && key_event)
- *frontend_key_event = *key_event;
- /* If a core calls RETRO_ENVIRONMENT_SET_KEYBOARD_CALLBACK,
- * then it is assumed that game focus mode is desired */
- p_rarch->game_focus_state.core_requested = true;
- break;
- }
- case RETRO_ENVIRONMENT_GET_DISK_CONTROL_INTERFACE_VERSION:
- RARCH_LOG("[Environ]: GET_DISK_CONTROL_INTERFACE_VERSION.\n");
- /* Current API version is 1 */
- *(unsigned *)data = 1;
- break;
- case RETRO_ENVIRONMENT_SET_DISK_CONTROL_INTERFACE:
- {
- const struct retro_disk_control_callback *control_cb =
- (const struct retro_disk_control_callback*)data;
- if (system)
- {
- RARCH_LOG("[Environ]: SET_DISK_CONTROL_INTERFACE.\n");
- disk_control_set_callback(&system->disk_control, control_cb);
- }
- }
- break;
- case RETRO_ENVIRONMENT_SET_DISK_CONTROL_EXT_INTERFACE:
- {
- const struct retro_disk_control_ext_callback *control_cb =
- (const struct retro_disk_control_ext_callback*)data;
- if (system)
- {
- RARCH_LOG("[Environ]: SET_DISK_CONTROL_EXT_INTERFACE.\n");
- disk_control_set_ext_callback(&system->disk_control, control_cb);
- }
- }
- break;
- case RETRO_ENVIRONMENT_GET_PREFERRED_HW_RENDER:
- {
- unsigned *cb = (unsigned*)data;
- settings_t *settings = p_rarch->configuration_settings;
- const char *video_driver_name = settings->arrays.video_driver;
- bool driver_switch_enable = settings->bools.driver_switch_enable;
- RARCH_LOG("[Environ]: GET_PREFERRED_HW_RENDER, video driver name: %s.\n", video_driver_name);
- if (string_is_equal(video_driver_name, "glcore"))
- {
- *cb = RETRO_HW_CONTEXT_OPENGL_CORE;
- RARCH_LOG("[Environ]: GET_PREFERRED_HW_RENDER - Context callback set to RETRO_HW_CONTEXT_OPENGL_CORE.\n");
- }
- else if (string_is_equal(video_driver_name, "gl"))
- {
- *cb = RETRO_HW_CONTEXT_OPENGL;
- RARCH_LOG("[Environ]: GET_PREFERRED_HW_RENDER - Context callback set to RETRO_HW_CONTEXT_OPENGL.\n");
- }
- else if (string_is_equal(video_driver_name, "vulkan"))
- {
- *cb = RETRO_HW_CONTEXT_VULKAN;
- RARCH_LOG("[Environ]: GET_PREFERRED_HW_RENDER - Context callback set to RETRO_HW_CONTEXT_VULKAN.\n");
- }
- else if (!strncmp(video_driver_name, "d3d", 3))
- {
- *cb = RETRO_HW_CONTEXT_DIRECT3D;
- RARCH_LOG("[Environ]: GET_PREFERRED_HW_RENDER - Context callback set to RETRO_HW_CONTEXT_DIRECT3D.\n");
- }
- else
- {
- *cb = RETRO_HW_CONTEXT_NONE;
- RARCH_LOG("[Environ]: GET_PREFERRED_HW_RENDER - Context callback set to RETRO_HW_CONTEXT_NONE.\n");
- }
- if (!driver_switch_enable)
- {
- RARCH_LOG("[Environ]: Driver switching disabled, GET_PREFERRED_HW_RENDER will be ignored.\n");
- return false;
- }
- break;
- }
- case RETRO_ENVIRONMENT_SET_HW_RENDER:
- case RETRO_ENVIRONMENT_SET_HW_RENDER | RETRO_ENVIRONMENT_EXPERIMENTAL:
- {
- struct retro_hw_render_callback *cb =
- (struct retro_hw_render_callback*)data;
- struct retro_hw_render_callback *hwr =
- VIDEO_DRIVER_GET_HW_CONTEXT_INTERNAL(p_rarch);
- if (!cb)
- {
- RARCH_ERR("[Environ]: SET_HW_RENDER - No valid callback passed, returning...\n");
- return false;
- }
- RARCH_LOG("[Environ]: SET_HW_RENDER, context type: %s.\n", hw_render_context_name(cb->context_type, cb->version_major, cb->version_minor));
- if (!dynamic_request_hw_context(
- cb->context_type, cb->version_minor, cb->version_major))
- {
- RARCH_ERR("[Environ]: SET_HW_RENDER - Dynamic request HW context failed.\n");
- return false;
- }
- if (!dynamic_verify_hw_context(p_rarch->configuration_settings,
- cb->context_type, cb->version_minor, cb->version_major))
- {
- RARCH_ERR("[Environ]: SET_HW_RENDER: Dynamic verify HW context failed.\n");
- return false;
- }
- #if defined(HAVE_OPENGL) || defined(HAVE_OPENGL_CORE)
- if (!gl_set_core_context(cb->context_type)) { }
- #endif
- cb->get_current_framebuffer = video_driver_get_current_framebuffer;
- cb->get_proc_address = video_driver_get_proc_address;
- /* Old ABI. Don't copy garbage. */
- if (cmd & RETRO_ENVIRONMENT_EXPERIMENTAL)
- {
- memcpy(hwr,
- cb, offsetof(struct retro_hw_render_callback, stencil));
- memset((uint8_t*)hwr + offsetof(struct retro_hw_render_callback, stencil),
- 0, sizeof(*cb) - offsetof(struct retro_hw_render_callback, stencil));
- }
- else
- memcpy(hwr, cb, sizeof(*cb));
- RARCH_LOG("Reached end of SET_HW_RENDER.\n");
- break;
- }
- case RETRO_ENVIRONMENT_SET_SUPPORT_NO_GAME:
- {
- bool state = *(const bool*)data;
- RARCH_LOG("[Environ]: SET_SUPPORT_NO_GAME: %s.\n", state ? "yes" : "no");
- if (state)
- content_set_does_not_need_content();
- else
- content_unset_does_not_need_content();
- break;
- }
- case RETRO_ENVIRONMENT_GET_LIBRETRO_PATH:
- {
- const char **path = (const char**)data;
- RARCH_LOG("[Environ]: GET_LIBRETRO_PATH.\n");
- #ifdef HAVE_DYNAMIC
- *path = path_get(RARCH_PATH_CORE);
- #else
- *path = NULL;
- #endif
- break;
- }
- case RETRO_ENVIRONMENT_SET_AUDIO_CALLBACK:
- #ifdef HAVE_THREADS
- {
- const struct retro_audio_callback *cb = (const struct retro_audio_callback*)data;
- RARCH_LOG("[Environ]: SET_AUDIO_CALLBACK.\n");
- #ifdef HAVE_NETWORKING
- if (netplay_driver_ctl(RARCH_NETPLAY_CTL_IS_ENABLED, NULL))
- return false;
- #endif
- if (p_rarch->recording_data) /* A/V sync is a must. */
- return false;
- if (cb)
- p_rarch->audio_callback = *cb;
- }
- break;
- #else
- return false;
- #endif
- case RETRO_ENVIRONMENT_SET_FRAME_TIME_CALLBACK:
- {
- const struct retro_frame_time_callback *info =
- (const struct retro_frame_time_callback*)data;
- RARCH_LOG("[Environ]: SET_FRAME_TIME_CALLBACK.\n");
- #ifdef HAVE_NETWORKING
- /* retro_run() will be called in very strange and
- * mysterious ways, have to disable it. */
- if (netplay_driver_ctl(RARCH_NETPLAY_CTL_IS_ENABLED, NULL))
- return false;
- #endif
- p_rarch->runloop_frame_time = *info;
- break;
- }
- case RETRO_ENVIRONMENT_SET_AUDIO_BUFFER_STATUS_CALLBACK:
- {
- const struct retro_audio_buffer_status_callback *info =
- (const struct retro_audio_buffer_status_callback*)data;
- RARCH_LOG("[Environ]: SET_AUDIO_BUFFER_STATUS_CALLBACK.\n");
- if (info)
- p_rarch->runloop_audio_buffer_status.callback = info->callback;
- else
- p_rarch->runloop_audio_buffer_status.callback = NULL;
- break;
- }
- case RETRO_ENVIRONMENT_SET_MINIMUM_AUDIO_LATENCY:
- {
- unsigned audio_latency_default = settings->uints.audio_latency;
- unsigned audio_latency_current =
- (p_rarch->runloop_audio_latency > audio_latency_default) ?
- p_rarch->runloop_audio_latency : audio_latency_default;
- unsigned audio_latency_new;
- RARCH_LOG("[Environ]: RETRO_ENVIRONMENT_SET_MINIMUM_AUDIO_LATENCY.\n");
- /* Sanitise input latency value */
- p_rarch->runloop_audio_latency = 0;
- if (data)
- p_rarch->runloop_audio_latency = *(const unsigned*)data;
- if (p_rarch->runloop_audio_latency > 512)
- {
- RARCH_WARN("[Environ]: Requested audio latency of %u ms - limiting to maximum of 512 ms.\n",
- p_rarch->runloop_audio_latency);
- p_rarch->runloop_audio_latency = 512;
- }
- /* Determine new set-point latency value */
- if (p_rarch->runloop_audio_latency >= audio_latency_default)
- audio_latency_new = p_rarch->runloop_audio_latency;
- else
- {
- if (p_rarch->runloop_audio_latency != 0)
- RARCH_WARN("[Environ]: Requested audio latency of %u ms is less than frontend default of %u ms."
- " Using frontend default...\n",
- p_rarch->runloop_audio_latency, audio_latency_default);
- audio_latency_new = audio_latency_default;
- }
- /* Check whether audio driver requires reinitialisation
- * (Identical to RETRO_ENVIRONMENT_SET_SYSTEM_AV_INFO,
- * without video driver initialisation) */
- if (audio_latency_new != audio_latency_current)
- {
- bool video_fullscreen = settings->bools.video_fullscreen;
- int reinit_flags = DRIVERS_CMD_ALL &
- ~(DRIVER_VIDEO_MASK | DRIVER_INPUT_MASK | DRIVER_MENU_MASK);
- RARCH_LOG("[Environ]: Setting audio latency to %u ms.\n", audio_latency_new);
- command_event(CMD_EVENT_REINIT, &reinit_flags);
- video_driver_set_aspect_ratio();
- /* Cannot continue recording with different parameters.
- * Take the easiest route out and just restart the recording. */
- if (p_rarch->recording_data)
- {
- runloop_msg_queue_push(
- msg_hash_to_str(MSG_RESTARTING_RECORDING_DUE_TO_DRIVER_REINIT),
- 2, 180, false,
- NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO);
- command_event(CMD_EVENT_RECORD_DEINIT, NULL);
- command_event(CMD_EVENT_RECORD_INIT, NULL);
- }
- /* Hide mouse cursor in fullscreen mode */
- if (video_fullscreen)
- video_driver_hide_mouse();
- }
- break;
- }
- case RETRO_ENVIRONMENT_GET_RUMBLE_INTERFACE:
- {
- struct retro_rumble_interface *iface =
- (struct retro_rumble_interface*)data;
- RARCH_LOG("[Environ]: GET_RUMBLE_INTERFACE.\n");
- iface->set_rumble_state = input_driver_set_rumble_state;
- break;
- }
- case RETRO_ENVIRONMENT_GET_INPUT_DEVICE_CAPABILITIES:
- {
- uint64_t *mask = (uint64_t*)data;
- RARCH_LOG("[Environ]: GET_INPUT_DEVICE_CAPABILITIES.\n");
- if (!p_rarch->current_input->get_capabilities || !p_rarch->current_input_data)
- return false;
- *mask = input_driver_get_capabilities();
- break;
- }
- case RETRO_ENVIRONMENT_GET_SENSOR_INTERFACE:
- {
- settings_t *settings = p_rarch->configuration_settings;
- bool input_sensors_enable = settings->bools.input_sensors_enable;
- struct retro_sensor_interface *iface = (struct retro_sensor_interface*)data;
- RARCH_LOG("[Environ]: GET_SENSOR_INTERFACE.\n");
- if (!input_sensors_enable)
- return false;
- iface->set_sensor_state = input_sensor_set_state;
- iface->get_sensor_input = input_sensor_get_input;
- break;
- }
- case RETRO_ENVIRONMENT_GET_CAMERA_INTERFACE:
- {
- struct retro_camera_callback *cb =
- (struct retro_camera_callback*)data;
- RARCH_LOG("[Environ]: GET_CAMERA_INTERFACE.\n");
- cb->start = driver_camera_start;
- cb->stop = driver_camera_stop;
- p_rarch->camera_cb = *cb;
- if (cb->caps != 0)
- p_rarch->camera_driver_active = true;
- else
- p_rarch->camera_driver_active = false;
- break;
- }
- case RETRO_ENVIRONMENT_GET_LOCATION_INTERFACE:
- {
- struct retro_location_callback *cb =
- (struct retro_location_callback*)data;
- RARCH_LOG("[Environ]: GET_LOCATION_INTERFACE.\n");
- cb->start = driver_location_start;
- cb->stop = driver_location_stop;
- cb->get_position = driver_location_get_position;
- cb->set_interval = driver_location_set_interval;
- if (system)
- system->location_cb = *cb;
- p_rarch->location_driver_active = false;
- break;
- }
- case RETRO_ENVIRONMENT_GET_LOG_INTERFACE:
- {
- struct retro_log_callback *cb = (struct retro_log_callback*)data;
- RARCH_LOG("[Environ]: GET_LOG_INTERFACE.\n");
- cb->log = rarch_log_libretro;
- break;
- }
- case RETRO_ENVIRONMENT_GET_PERF_INTERFACE:
- {
- struct retro_perf_callback *cb = (struct retro_perf_callback*)data;
- RARCH_LOG("[Environ]: GET_PERF_INTERFACE.\n");
- cb->get_time_usec = cpu_features_get_time_usec;
- cb->get_cpu_features = cpu_features_get;
- cb->get_perf_counter = cpu_features_get_perf_counter;
- cb->perf_register = performance_counter_register;
- cb->perf_start = core_performance_counter_start;
- cb->perf_stop = core_performance_counter_stop;
- cb->perf_log = retro_perf_log;
- break;
- }
- case RETRO_ENVIRONMENT_GET_CORE_ASSETS_DIRECTORY:
- {
- const char **dir = (const char**)data;
- const char *dir_core_assets = settings->paths.directory_core_assets;
- *dir = *dir_core_assets ?
- dir_core_assets : NULL;
- RARCH_LOG("[Environ]: CORE_ASSETS_DIRECTORY: \"%s\".\n",
- dir_core_assets);
- break;
- }
- case RETRO_ENVIRONMENT_SET_SYSTEM_AV_INFO:
- /**
- * Update the system Audio/Video information.
- * Will reinitialize audio/video drivers if needed.
- * Used by RETRO_ENVIRONMENT_SET_SYSTEM_AV_INFO.
- **/
- {
- const struct retro_system_av_info **info = (const struct retro_system_av_info**)&data;
- struct retro_system_av_info *av_info = &p_rarch->video_driver_av_info;
- if (data)
- {
- settings_t *settings = p_rarch->configuration_settings;
- unsigned crt_switch_resolution = settings->uints.crt_switch_resolution;
- bool video_fullscreen = settings->bools.video_fullscreen;
- const bool no_video_reinit = (
- crt_switch_resolution == 0
- && data
- && ((*info)->geometry.max_width == av_info->geometry.max_width)
- && ((*info)->geometry.max_height == av_info->geometry.max_height));
- /* When not doing video reinit, we also must not do input and menu
- * reinit, otherwise the input driver crashes and the menu gets
- * corrupted. */
- int reinit_flags = no_video_reinit ?
- DRIVERS_CMD_ALL & ~(DRIVER_VIDEO_MASK | DRIVER_INPUT_MASK | DRIVER_MENU_MASK)
- : DRIVERS_CMD_ALL;
- RARCH_LOG("[Environ]: SET_SYSTEM_AV_INFO.\n");
- memcpy(av_info, *info, sizeof(*av_info));
- command_event(CMD_EVENT_REINIT, &reinit_flags);
- if (no_video_reinit)
- video_driver_set_aspect_ratio();
- /* Cannot continue recording with different parameters.
- * Take the easiest route out and just restart the recording. */
- if (p_rarch->recording_data)
- {
- runloop_msg_queue_push(
- msg_hash_to_str(MSG_RESTARTING_RECORDING_DUE_TO_DRIVER_REINIT),
- 2, 180, false,
- NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO);
- command_event(CMD_EVENT_RECORD_DEINIT, NULL);
- command_event(CMD_EVENT_RECORD_INIT, NULL);
- }
- /* Hide mouse cursor in fullscreen after
- * a RETRO_ENVIRONMENT_SET_SYSTEM_AV_INFO call. */
- if (video_fullscreen)
- video_driver_hide_mouse();
- return true;
- }
- return false;
- }
- case RETRO_ENVIRONMENT_SET_SUBSYSTEM_INFO:
- {
- unsigned i;
- const struct retro_subsystem_info *info =
- (const struct retro_subsystem_info*)data;
- unsigned log_level = settings->uints.libretro_log_level;
- if (log_level == RETRO_LOG_DEBUG)
- RARCH_LOG("[Environ]: SET_SUBSYSTEM_INFO.\n");
- for (i = 0; info[i].ident; i++)
- {
- unsigned j;
- if (log_level != RETRO_LOG_DEBUG)
- continue;
- RARCH_LOG("Special game type: %s\n Ident: %s\n ID: %u\n Content:\n",
- info[i].desc,
- info[i].ident,
- info[i].id
- );
- for (j = 0; j < info[i].num_roms; j++)
- {
- RARCH_LOG(" %s (%s)\n",
- info[i].roms[j].desc, info[i].roms[j].required ?
- "required" : "optional");
- }
- }
- if (system)
- {
- struct retro_subsystem_info *info_ptr = NULL;
- free(system->subsystem.data);
- system->subsystem.data = NULL;
- system->subsystem.size = 0;
- info_ptr = (struct retro_subsystem_info*)
- malloc(i * sizeof(*info_ptr));
- if (!info_ptr)
- return false;
- system->subsystem.data = info_ptr;
- memcpy(system->subsystem.data, info,
- i * sizeof(*system->subsystem.data));
- system->subsystem.size = i;
- p_rarch->current_core.has_set_subsystems = true;
- }
- break;
- }
- case RETRO_ENVIRONMENT_SET_CONTROLLER_INFO:
- {
- unsigned i, j;
- const struct retro_controller_info *info =
- (const struct retro_controller_info*)data;
- unsigned log_level = settings->uints.libretro_log_level;
- RARCH_LOG("[Environ]: SET_CONTROLLER_INFO.\n");
- for (i = 0; info[i].types; i++)
- {
- if (log_level != RETRO_LOG_DEBUG)
- continue;
- RARCH_LOG("Controller port: %u\n", i + 1);
- for (j = 0; j < info[i].num_types; j++)
- RARCH_LOG(" %s (ID: %u)\n", info[i].types[j].desc,
- info[i].types[j].id);
- }
- if (system)
- {
- struct retro_controller_info *info_ptr = NULL;
- free(system->ports.data);
- system->ports.data = NULL;
- system->ports.size = 0;
- info_ptr = (struct retro_controller_info*)calloc(i, sizeof(*info_ptr));
- if (!info_ptr)
- return false;
- system->ports.data = info_ptr;
- memcpy(system->ports.data, info,
- i * sizeof(*system->ports.data));
- system->ports.size = i;
- }
- break;
- }
- case RETRO_ENVIRONMENT_SET_MEMORY_MAPS:
- {
- if (system)
- {
- unsigned i;
- const struct retro_memory_map *mmaps =
- (const struct retro_memory_map*)data;
- rarch_memory_descriptor_t *descriptors = NULL;
- RARCH_LOG("[Environ]: SET_MEMORY_MAPS.\n");
- free((void*)system->mmaps.descriptors);
- system->mmaps.descriptors = 0;
- system->mmaps.num_descriptors = 0;
- descriptors = (rarch_memory_descriptor_t*)
- calloc(mmaps->num_descriptors,
- sizeof(*descriptors));
- if (!descriptors)
- return false;
- system->mmaps.descriptors = descriptors;
- system->mmaps.num_descriptors = mmaps->num_descriptors;
- for (i = 0; i < mmaps->num_descriptors; i++)
- system->mmaps.descriptors[i].core = mmaps->descriptors[i];
- mmap_preprocess_descriptors(descriptors, mmaps->num_descriptors);
- if (sizeof(void *) == 8)
- RARCH_LOG(" ndx flags ptr offset start select disconn len addrspace\n");
- else
- RARCH_LOG(" ndx flags ptr offset start select disconn len addrspace\n");
- for (i = 0; i < system->mmaps.num_descriptors; i++)
- {
- const rarch_memory_descriptor_t *desc =
- &system->mmaps.descriptors[i];
- char flags[7];
- flags[0] = 'M';
- if ((desc->core.flags & RETRO_MEMDESC_MINSIZE_8) == RETRO_MEMDESC_MINSIZE_8)
- flags[1] = '8';
- else if ((desc->core.flags & RETRO_MEMDESC_MINSIZE_4) == RETRO_MEMDESC_MINSIZE_4)
- flags[1] = '4';
- else if ((desc->core.flags & RETRO_MEMDESC_MINSIZE_2) == RETRO_MEMDESC_MINSIZE_2)
- flags[1] = '2';
- else
- flags[1] = '1';
- flags[2] = 'A';
- if ((desc->core.flags & RETRO_MEMDESC_ALIGN_8) == RETRO_MEMDESC_ALIGN_8)
- flags[3] = '8';
- else if ((desc->core.flags & RETRO_MEMDESC_ALIGN_4) == RETRO_MEMDESC_ALIGN_4)
- flags[3] = '4';
- else if ((desc->core.flags & RETRO_MEMDESC_ALIGN_2) == RETRO_MEMDESC_ALIGN_2)
- flags[3] = '2';
- else
- flags[3] = '1';
- flags[4] = (desc->core.flags & RETRO_MEMDESC_BIGENDIAN) ? 'B' : 'b';
- flags[5] = (desc->core.flags & RETRO_MEMDESC_CONST) ? 'C' : 'c';
- flags[6] = 0;
- RARCH_LOG(" %03u %s %p %08X %08X %08X %08X %08X %s\n",
- i + 1, flags, desc->core.ptr, desc->core.offset, desc->core.start,
- desc->core.select, desc->core.disconnect, desc->core.len,
- desc->core.addrspace ? desc->core.addrspace : "");
- }
- }
- else
- {
- RARCH_WARN("[Environ]: SET_MEMORY_MAPS, but system pointer not initialized..\n");
- }
- break;
- }
- case RETRO_ENVIRONMENT_SET_GEOMETRY:
- {
- struct retro_system_av_info *av_info = &p_rarch->video_driver_av_info;
- struct retro_game_geometry *geom = (struct retro_game_geometry*)&av_info->geometry;
- const struct retro_game_geometry *in_geom = (const struct retro_game_geometry*)data;
- if (!geom)
- return false;
- /* Can potentially be called every frame,
- * don't do anything unless required. */
- if ( (geom->base_width != in_geom->base_width) ||
- (geom->base_height != in_geom->base_height) ||
- (geom->aspect_ratio != in_geom->aspect_ratio))
- {
- geom->base_width = in_geom->base_width;
- geom->base_height = in_geom->base_height;
- geom->aspect_ratio = in_geom->aspect_ratio;
- RARCH_LOG("[Environ]: SET_GEOMETRY: %ux%u, aspect: %.3f.\n",
- geom->base_width, geom->base_height, geom->aspect_ratio);
- /* Forces recomputation of aspect ratios if
- * using core-dependent aspect ratios. */
- video_driver_set_aspect_ratio();
- /* TODO: Figure out what to do, if anything, with recording. */
- }
- else
- {
- RARCH_LOG("[Environ]: SET_GEOMETRY.\n");
- }
- break;
- }
- case RETRO_ENVIRONMENT_GET_CURRENT_SOFTWARE_FRAMEBUFFER:
- {
- struct retro_framebuffer *fb = (struct retro_framebuffer*)data;
- if (
- p_rarch->video_driver_poke
- && p_rarch->video_driver_poke->get_current_software_framebuffer
- && p_rarch->video_driver_poke->get_current_software_framebuffer(
- p_rarch->video_driver_data, fb))
- return true;
- return false;
- }
- case RETRO_ENVIRONMENT_GET_HW_RENDER_INTERFACE:
- {
- const struct retro_hw_render_interface **iface = (const struct retro_hw_render_interface **)data;
- if (
- p_rarch->video_driver_poke
- && p_rarch->video_driver_poke->get_hw_render_interface
- && p_rarch->video_driver_poke->get_hw_render_interface(
- p_rarch->video_driver_data, iface))
- return true;
- return false;
- }
- case RETRO_ENVIRONMENT_SET_SUPPORT_ACHIEVEMENTS:
- #ifdef HAVE_CHEEVOS
- {
- bool state = *(const bool*)data;
- RARCH_LOG("[Environ]: SET_SUPPORT_ACHIEVEMENTS: %s.\n", state ? "yes" : "no");
- rcheevos_set_support_cheevos(state);
- }
- #endif
- break;
- case RETRO_ENVIRONMENT_SET_HW_RENDER_CONTEXT_NEGOTIATION_INTERFACE:
- {
- const struct retro_hw_render_context_negotiation_interface *iface =
- (const struct retro_hw_render_context_negotiation_interface*)data;
- RARCH_LOG("[Environ]: SET_HW_RENDER_CONTEXT_NEGOTIATION_INTERFACE.\n");
- p_rarch->hw_render_context_negotiation = iface;
- break;
- }
- case RETRO_ENVIRONMENT_SET_SERIALIZATION_QUIRKS:
- {
- uint64_t *quirks = (uint64_t *) data;
- RARCH_LOG("[Environ]: SET_SERIALIZATION_QUIRKS.\n");
- p_rarch->current_core.serialization_quirks_v = *quirks;
- break;
- }
- case RETRO_ENVIRONMENT_SET_HW_SHARED_CONTEXT:
- #ifdef HAVE_LIBNX
- RARCH_LOG("[Environ]: SET_HW_SHARED_CONTEXT - ignored for now.\n");
- /* TODO/FIXME - Force this off for now for Switch
- * until shared HW context can work there */
- return false;
- #else
- RARCH_LOG("[Environ]: SET_HW_SHARED_CONTEXT.\n");
- p_rarch->core_set_shared_context = true;
- #endif
- break;
- case RETRO_ENVIRONMENT_GET_VFS_INTERFACE:
- {
- const uint32_t supported_vfs_version = 3;
- static struct retro_vfs_interface vfs_iface =
- {
- /* VFS API v1 */
- retro_vfs_file_get_path_impl,
- retro_vfs_file_open_impl,
- retro_vfs_file_close_impl,
- retro_vfs_file_size_impl,
- retro_vfs_file_tell_impl,
- retro_vfs_file_seek_impl,
- retro_vfs_file_read_impl,
- retro_vfs_file_write_impl,
- retro_vfs_file_flush_impl,
- retro_vfs_file_remove_impl,
- retro_vfs_file_rename_impl,
- /* VFS API v2 */
- retro_vfs_file_truncate_impl,
- /* VFS API v3 */
- retro_vfs_stat_impl,
- retro_vfs_mkdir_impl,
- retro_vfs_opendir_impl,
- retro_vfs_readdir_impl,
- retro_vfs_dirent_get_name_impl,
- retro_vfs_dirent_is_dir_impl,
- retro_vfs_closedir_impl
- };
- struct retro_vfs_interface_info *vfs_iface_info = (struct retro_vfs_interface_info *) data;
- if (vfs_iface_info->required_interface_version <= supported_vfs_version)
- {
- RARCH_LOG("Core requested VFS version >= v%d, providing v%d\n", vfs_iface_info->required_interface_version, supported_vfs_version);
- vfs_iface_info->required_interface_version = supported_vfs_version;
- vfs_iface_info->iface = &vfs_iface;
- system->supports_vfs = true;
- }
- else
- {
- RARCH_WARN("Core requested VFS version v%d which is higher than what we support (v%d)\n", vfs_iface_info->required_interface_version, supported_vfs_version);
- return false;
- }
- break;
- }
- case RETRO_ENVIRONMENT_GET_LED_INTERFACE:
- {
- struct retro_led_interface *ledintf =
- (struct retro_led_interface *)data;
- if (ledintf)
- ledintf->set_led_state = led_driver_set_led;
- }
- break;
- case RETRO_ENVIRONMENT_GET_AUDIO_VIDEO_ENABLE:
- {
- int result = 0;
- if ( !p_rarch->audio_suspended &&
- p_rarch->audio_driver_active)
- result |= 2;
- if (p_rarch->video_driver_active
- && !(p_rarch->current_video->frame == video_null.frame))
- result |= 1;
- #ifdef HAVE_RUNAHEAD
- if (p_rarch->request_fast_savestate)
- result |= 4;
- if (p_rarch->hard_disable_audio)
- result |= 8;
- #endif
- #ifdef HAVE_NETWORKING
- if (netplay_driver_ctl(RARCH_NETPLAY_CTL_IS_REPLAYING, NULL))
- result &= ~(1|2);
- if (netplay_driver_ctl(RARCH_NETPLAY_CTL_IS_ENABLED, NULL))
- result |= 4;
- #endif
- if (data)
- {
- int* result_p = (int*)data;
- *result_p = result;
- }
- break;
- }
- case RETRO_ENVIRONMENT_GET_MIDI_INTERFACE:
- {
- struct retro_midi_interface *midi_interface =
- (struct retro_midi_interface *)data;
- if (midi_interface)
- {
- midi_interface->input_enabled = midi_driver_input_enabled;
- midi_interface->output_enabled = midi_driver_output_enabled;
- midi_interface->read = midi_driver_read;
- midi_interface->write = midi_driver_write;
- midi_interface->flush = midi_driver_flush;
- }
- break;
- }
- case RETRO_ENVIRONMENT_GET_FASTFORWARDING:
- *(bool *)data = p_rarch->runloop_fastmotion;
- break;
- case RETRO_ENVIRONMENT_GET_INPUT_BITMASKS:
- /* Just falldown, the function will return true */
- break;
- case RETRO_ENVIRONMENT_GET_CORE_OPTIONS_VERSION:
- RARCH_LOG("[Environ]: GET_CORE_OPTIONS_VERSION.\n");
- /* Current API version is 1 */
- *(unsigned *)data = 1;
- break;
- case RETRO_ENVIRONMENT_GET_TARGET_REFRESH_RATE:
- {
- /* Try to use the polled refresh rate first. */
- float target_refresh_rate = video_driver_get_refresh_rate();
- float video_refresh_rate = settings ? settings->floats.video_refresh_rate : 0.0;
- /* If the above function failed [possibly because it is not
- * implemented], use the refresh rate set in the config instead. */
- if (target_refresh_rate == 0.0f && video_refresh_rate != 0.0f)
- target_refresh_rate = video_refresh_rate;
- *(float *)data = target_refresh_rate;
- break;
- }
- case RETRO_ENVIRONMENT_GET_INPUT_MAX_USERS:
- *(unsigned *)data = p_rarch->input_driver_max_users;
- break;
- /* Private environment callbacks.
- *
- * Should all be properly addressed in version 2.
- * */
- case RETRO_ENVIRONMENT_POLL_TYPE_OVERRIDE:
- {
- const unsigned *poll_type_data = (const unsigned*)data;
- if (poll_type_data)
- p_rarch->core_poll_type_override = (enum poll_type_override_t)*poll_type_data;
- }
- break;
- case RETRO_ENVIRONMENT_GET_CLEAR_ALL_THREAD_WAITS_CB:
- *(retro_environment_t *)data = rarch_clear_all_thread_waits;
- break;
- case RETRO_ENVIRONMENT_SET_SAVE_STATE_IN_BACKGROUND:
- {
- bool state = *(const bool*)data;
- RARCH_LOG("[Environ]: SET_SAVE_STATE_IN_BACKGROUND: %s.\n", state ? "yes" : "no");
- set_save_state_in_background(state);
- }
- break;
- default:
- RARCH_LOG("[Environ]: UNSUPPORTED (#%u).\n", cmd);
- return false;
- }
- return true;
- }
- #ifdef HAVE_DYNAMIC
- /**
- * libretro_get_environment_info:
- * @func : Function pointer for get_environment_info.
- * @load_no_content : If true, core should be able to auto-start
- * without any content loaded.
- *
- * Sets environment callback in order to get statically known
- * information from it.
- *
- * Fetched via environment callbacks instead of
- * retro_get_system_info(), as this info is part of extensions.
- *
- * Should only be called once right after core load to
- * avoid overwriting the "real" environ callback.
- *
- * For statically linked cores, pass retro_set_environment as argument.
- */
- static void libretro_get_environment_info(
- void (*func)(retro_environment_t),
- bool *load_no_content)
- {
- struct rarch_state *p_rarch = &rarch_st;
- p_rarch->load_no_content_hook = load_no_content;
- /* load_no_content gets set in this callback. */
- func(environ_cb_get_system_info);
- /* It's possible that we just set get_system_info callback
- * to the currently running core.
- *
- * Make sure we reset it to the actual environment callback.
- * Ignore any environment callbacks here in case we're running
- * on the non-current core. */
- p_rarch->ignore_environment_cb = true;
- func(rarch_environment_cb);
- p_rarch->ignore_environment_cb = false;
- }
- static dylib_t load_dynamic_core(
- const char *path, char *buf, size_t size)
- {
- #if defined(ANDROID)
- /* Can't resolve symlinks when dealing with cores
- * installed via play feature delivery, because the
- * source files have non-standard file names (which
- * will not be recognised by regular core handling
- * routines) */
- bool resolve_symlinks = !play_feature_delivery_enabled();
- #else
- bool resolve_symlinks = true;
- #endif
- /* Can't lookup symbols in itself on UWP */
- #if !(defined(__WINRT__) || defined(WINAPI_FAMILY) && WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP)
- if (dylib_proc(NULL, "retro_init"))
- {
- /* Try to verify that -lretro was not linked in from other modules
- * since loading it dynamically and with -l will fail hard. */
- RARCH_ERR("Serious problem. RetroArch wants to load libretro cores"
- " dynamically, but it is already linked.\n");
- RARCH_ERR("This could happen if other modules RetroArch depends on "
- "link against libretro directly.\n");
- RARCH_ERR("Proceeding could cause a crash. Aborting ...\n");
- retroarch_fail(1, "init_libretro_symbols()");
- }
- #endif
- /* Need to use absolute path for this setting. It can be
- * saved to content history, and a relative path would
- * break in that scenario. */
- path_resolve_realpath(buf, size, resolve_symlinks);
- return dylib_load(path);
- }
- static dylib_t libretro_get_system_info_lib(const char *path,
- struct retro_system_info *info, bool *load_no_content)
- {
- dylib_t lib = dylib_load(path);
- void (*proc)(struct retro_system_info*);
- if (!lib)
- return NULL;
- proc = (void (*)(struct retro_system_info*))
- dylib_proc(lib, "retro_get_system_info");
- if (!proc)
- {
- dylib_close(lib);
- return NULL;
- }
- proc(info);
- if (load_no_content)
- {
- void (*set_environ)(retro_environment_t);
- *load_no_content = false;
- set_environ = (void (*)(retro_environment_t))
- dylib_proc(lib, "retro_set_environment");
- if (set_environ)
- libretro_get_environment_info(set_environ, load_no_content);
- }
- return lib;
- }
- #endif
- /**
- * libretro_get_system_info:
- * @path : Path to libretro library.
- * @info : Pointer to system info information.
- * @load_no_content : If true, core should be able to auto-start
- * without any content loaded.
- *
- * Gets system info from an arbitrary lib.
- * The struct returned must be freed as strings are allocated dynamically.
- *
- * Returns: true (1) if successful, otherwise false (0).
- **/
- static bool libretro_get_system_info(
- struct rarch_state *p_rarch,
- const char *path,
- struct retro_system_info *info,
- bool *load_no_content)
- {
- struct retro_system_info dummy_info;
- #ifdef HAVE_DYNAMIC
- dylib_t lib;
- #endif
- if (string_ends_with_size(path,
- "builtin", strlen(path), STRLEN_CONST("builtin")))
- return false;
- dummy_info.library_name = NULL;
- dummy_info.library_version = NULL;
- dummy_info.valid_extensions = NULL;
- dummy_info.need_fullpath = false;
- dummy_info.block_extract = false;
- #ifdef HAVE_DYNAMIC
- lib = libretro_get_system_info_lib(
- path, &dummy_info, load_no_content);
- if (!lib)
- {
- RARCH_ERR("%s: \"%s\"\n",
- msg_hash_to_str(MSG_FAILED_TO_OPEN_LIBRETRO_CORE),
- path);
- RARCH_ERR("Error(s): %s\n", dylib_error());
- return false;
- }
- #else
- if (load_no_content)
- {
- p_rarch->load_no_content_hook = load_no_content;
- /* load_no_content gets set in this callback. */
- retro_set_environment(environ_cb_get_system_info);
- /* It's possible that we just set get_system_info callback
- * to the currently running core.
- *
- * Make sure we reset it to the actual environment callback.
- * Ignore any environment callbacks here in case we're running
- * on the non-current core. */
- p_rarch->ignore_environment_cb = true;
- retro_set_environment(rarch_environment_cb);
- p_rarch->ignore_environment_cb = false;
- }
- retro_get_system_info(&dummy_info);
- #endif
- memcpy(info, &dummy_info, sizeof(*info));
- p_rarch->current_library_name[0] = '\0';
- p_rarch->current_library_version[0] = '\0';
- p_rarch->current_valid_extensions[0] = '\0';
- if (!string_is_empty(dummy_info.library_name))
- strlcpy(p_rarch->current_library_name,
- dummy_info.library_name, sizeof(p_rarch->current_library_name));
- if (!string_is_empty(dummy_info.library_version))
- strlcpy(p_rarch->current_library_version,
- dummy_info.library_version, sizeof(p_rarch->current_library_version));
- if (dummy_info.valid_extensions)
- strlcpy(p_rarch->current_valid_extensions,
- dummy_info.valid_extensions, sizeof(p_rarch->current_valid_extensions));
- info->library_name = p_rarch->current_library_name;
- info->library_version = p_rarch->current_library_version;
- info->valid_extensions = p_rarch->current_valid_extensions;
- #ifdef HAVE_DYNAMIC
- dylib_close(lib);
- #endif
- return true;
- }
- /**
- * load_symbols:
- * @type : Type of core to be loaded.
- * If CORE_TYPE_DUMMY, will
- * load dummy symbols.
- *
- * Setup libretro callback symbols. Returns true on success,
- * or false if symbols could not be loaded.
- **/
- static bool init_libretro_symbols_custom(
- struct rarch_state *p_rarch,
- enum rarch_core_type type,
- struct retro_core_t *current_core,
- const char *lib_path,
- void *_lib_handle_p)
- {
- #ifdef HAVE_DYNAMIC
- /* the library handle for use with the SYMBOL macro */
- dylib_t lib_handle_local;
- #endif
- switch (type)
- {
- case CORE_TYPE_PLAIN:
- {
- #ifdef HAVE_DYNAMIC
- #ifdef HAVE_RUNAHEAD
- dylib_t *lib_handle_p = (dylib_t*)_lib_handle_p;
- if (!lib_path || !lib_handle_p)
- #endif
- {
- const char *path = path_get(RARCH_PATH_CORE);
- if (string_is_empty(path))
- {
- RARCH_ERR("[Core]: Frontend is built for dynamic libretro cores, but "
- "path is not set. Cannot continue.\n");
- retroarch_fail(1, "init_libretro_symbols()");
- }
- RARCH_LOG("[Core]: Loading dynamic libretro core from: \"%s\"\n",
- path);
- if (!(p_rarch->lib_handle = load_dynamic_core(
- path,
- path_get_ptr(RARCH_PATH_CORE),
- path_get_realsize(RARCH_PATH_CORE)
- )))
- {
- RARCH_ERR("%s: \"%s\"\nError(s): %s\n",
- msg_hash_to_str(MSG_FAILED_TO_OPEN_LIBRETRO_CORE),
- path, dylib_error());
- runloop_msg_queue_push(msg_hash_to_str(MSG_FAILED_TO_OPEN_LIBRETRO_CORE),
- 1, 180, true, NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO);
- return false;
- }
- lib_handle_local = p_rarch->lib_handle;
- }
- #ifdef HAVE_RUNAHEAD
- else
- {
- /* for a secondary core, we already have a
- * primary library loaded, so we can skip
- * some checks and just load the library */
- retro_assert(lib_path != NULL && lib_handle_p != NULL);
- lib_handle_local = dylib_load(lib_path);
- if (!lib_handle_local)
- return false;
- *lib_handle_p = lib_handle_local;
- }
- #endif
- #endif
- CORE_SYMBOLS(SYMBOL);
- }
- break;
- case CORE_TYPE_DUMMY:
- CORE_SYMBOLS(SYMBOL_DUMMY);
- break;
- case CORE_TYPE_FFMPEG:
- #ifdef HAVE_FFMPEG
- CORE_SYMBOLS(SYMBOL_FFMPEG);
- #endif
- break;
- case CORE_TYPE_MPV:
- #ifdef HAVE_MPV
- CORE_SYMBOLS(SYMBOL_MPV);
- #endif
- break;
- case CORE_TYPE_IMAGEVIEWER:
- #ifdef HAVE_IMAGEVIEWER
- CORE_SYMBOLS(SYMBOL_IMAGEVIEWER);
- #endif
- break;
- case CORE_TYPE_NETRETROPAD:
- #if defined(HAVE_NETWORKING) && defined(HAVE_NETWORKGAMEPAD)
- CORE_SYMBOLS(SYMBOL_NETRETROPAD);
- #endif
- break;
- case CORE_TYPE_VIDEO_PROCESSOR:
- #if defined(HAVE_VIDEOPROCESSOR)
- CORE_SYMBOLS(SYMBOL_VIDEOPROCESSOR);
- #endif
- break;
- case CORE_TYPE_GONG:
- #ifdef HAVE_GONG
- CORE_SYMBOLS(SYMBOL_GONG);
- #endif
- break;
- }
- return true;
- }
- /**
- * init_libretro_symbols:
- * @type : Type of core to be loaded.
- * If CORE_TYPE_DUMMY, will
- * load dummy symbols.
- *
- * Initializes libretro symbols and
- * setups environment callback functions. Returns true on success,
- * or false if symbols could not be loaded.
- **/
- static bool init_libretro_symbols(
- struct rarch_state *p_rarch,
- enum rarch_core_type type,
- struct retro_core_t *current_core)
- {
- /* Load symbols */
- if (!init_libretro_symbols_custom(p_rarch,
- type, current_core, NULL, NULL))
- return false;
- #ifdef HAVE_RUNAHEAD
- /* remember last core type created, so creating a
- * secondary core will know what core type to use. */
- p_rarch->last_core_type = type;
- #endif
- return true;
- }
- bool libretro_get_shared_context(void)
- {
- struct rarch_state *p_rarch = &rarch_st;
- bool core_set_shared_context = p_rarch->core_set_shared_context;
- return core_set_shared_context;
- }
- /**
- * uninit_libretro_sym:
- *
- * Frees libretro core.
- *
- * Frees all core options,
- * associated state, and
- * unbind all libretro callback symbols.
- **/
- static void uninit_libretro_symbols(
- struct rarch_state *p_rarch,
- struct retro_core_t *current_core)
- {
- #ifdef HAVE_DYNAMIC
- if (p_rarch->lib_handle)
- dylib_close(p_rarch->lib_handle);
- p_rarch->lib_handle = NULL;
- #endif
- memset(current_core, 0, sizeof(struct retro_core_t));
- p_rarch->core_set_shared_context = false;
- if (p_rarch->runloop_core_options)
- retroarch_deinit_core_options(p_rarch);
- retroarch_system_info_free(p_rarch);
- retroarch_frame_time_free(p_rarch);
- retroarch_audio_buffer_status_free(p_rarch);
- retroarch_game_focus_free(p_rarch);
- p_rarch->camera_driver_active = false;
- p_rarch->location_driver_active = false;
- /* Performance counters no longer valid. */
- p_rarch->perf_ptr_libretro = 0;
- memset(p_rarch->perf_counters_libretro, 0,
- sizeof(p_rarch->perf_counters_libretro));
- }
- #if defined(HAVE_RUNAHEAD)
- static void free_retro_ctx_load_content_info(struct
- retro_ctx_load_content_info *dest)
- {
- if (!dest)
- return;
- core_free_retro_game_info(dest->info);
- string_list_free((struct string_list*)dest->content);
- if (dest->info)
- free(dest->info);
- dest->info = NULL;
- dest->content = NULL;
- }
- static struct retro_game_info* clone_retro_game_info(const
- struct retro_game_info *src)
- {
- struct retro_game_info *dest = (struct retro_game_info*)malloc(
- sizeof(struct retro_game_info));
- if (!dest)
- return NULL;
- dest->path = NULL;
- dest->data = NULL;
- dest->size = 0;
- dest->meta = NULL;
- if (src->size && src->data)
- {
- void *data = malloc(src->size);
- if (data)
- {
- memcpy(data, src->data, src->size);
- dest->data = data;
- }
- }
- if (!string_is_empty(src->path))
- dest->path = strdup(src->path);
- if (!string_is_empty(src->meta))
- dest->meta = strdup(src->meta);
- dest->size = src->size;
- return dest;
- }
- static struct retro_ctx_load_content_info
- *clone_retro_ctx_load_content_info(
- const struct retro_ctx_load_content_info *src)
- {
- struct retro_ctx_load_content_info *dest = NULL;
- if (!src || src->special)
- return NULL; /* refuse to deal with the Special field */
- dest = (struct retro_ctx_load_content_info*)
- malloc(sizeof(*dest));
- if (!dest)
- return NULL;
- dest->info = NULL;
- dest->content = NULL;
- dest->special = NULL;
- if (src->info)
- dest->info = clone_retro_game_info(src->info);
- if (src->content)
- dest->content = string_list_clone(src->content);
- return dest;
- }
- static void set_load_content_info(
- struct rarch_state *p_rarch,
- const retro_ctx_load_content_info_t *ctx)
- {
- free_retro_ctx_load_content_info(p_rarch->load_content_info);
- free(p_rarch->load_content_info);
- p_rarch->load_content_info = clone_retro_ctx_load_content_info(ctx);
- }
- /* RUNAHEAD - SECONDARY CORE */
- #if defined(HAVE_DYNAMIC) || defined(HAVE_DYLIB)
- static void strcat_alloc(char **dst, const char *s)
- {
- size_t len1;
- char *src = *dst;
- if (!src)
- {
- src = (s) ? strcpy_alloc(s) : (char*)calloc(1,1);
- *dst = src;
- return;
- }
- if (!s)
- return;
- len1 = strlen(src);
- src = (char*)realloc(src, len1 + strlen(s) + 1);
- if (!src)
- return;
- *dst = src;
- strcpy(src + len1, s);
- }
- static void secondary_core_destroy(struct rarch_state *p_rarch)
- {
- if (!p_rarch || !p_rarch->secondary_lib_handle)
- return;
- /* unload game from core */
- if (p_rarch->secondary_core.retro_unload_game)
- p_rarch->secondary_core.retro_unload_game();
- p_rarch->core_poll_type_override = POLL_TYPE_OVERRIDE_DONTCARE;
- /* deinit */
- if (p_rarch->secondary_core.retro_deinit)
- p_rarch->secondary_core.retro_deinit();
- memset(&p_rarch->secondary_core, 0, sizeof(struct retro_core_t));
- dylib_close(p_rarch->secondary_lib_handle);
- p_rarch->secondary_lib_handle = NULL;
- filestream_delete(p_rarch->secondary_library_path);
- if (p_rarch->secondary_library_path)
- free(p_rarch->secondary_library_path);
- p_rarch->secondary_library_path = NULL;
- }
- static bool secondary_core_ensure_exists(struct rarch_state *p_rarch)
- {
- if (!p_rarch->secondary_lib_handle)
- if (!secondary_core_create(p_rarch))
- return false;
- return true;
- }
- #if defined(HAVE_RUNAHEAD) && defined(HAVE_DYNAMIC)
- static bool secondary_core_deserialize(
- struct rarch_state *p_rarch,
- const void *buffer, int size)
- {
- if (secondary_core_ensure_exists(p_rarch))
- return p_rarch->secondary_core.retro_unserialize(buffer, size);
- secondary_core_destroy(p_rarch);
- return false;
- }
- #endif
- static void remember_controller_port_device(
- struct rarch_state *p_rarch,
- long port, long device)
- {
- if (port >= 0 && port < MAX_USERS)
- p_rarch->port_map[port] = (int)device;
- if ( p_rarch->secondary_lib_handle
- && p_rarch->secondary_core.retro_set_controller_port_device)
- p_rarch->secondary_core.retro_set_controller_port_device((unsigned)port, (unsigned)device);
- }
- static void clear_controller_port_map(struct rarch_state *p_rarch)
- {
- unsigned port;
- for (port = 0; port < MAX_USERS; port++)
- p_rarch->port_map[port] = -1;
- }
- static char *get_temp_directory_alloc(const char *override_dir)
- {
- const char *src = NULL;
- char *path = NULL;
- #ifdef _WIN32
- #ifdef LEGACY_WIN32
- DWORD plen = GetTempPath(0, NULL) + 1;
- if (!(path = (char*)malloc(plen * sizeof(char))))
- return NULL;
- path[plen - 1] = 0;
- GetTempPath(plen, path);
- #else
- DWORD plen = GetTempPathW(0, NULL) + 1;
- wchar_t *wide_str = (wchar_t*)malloc(plen * sizeof(wchar_t));
- if (!wide_str)
- return NULL;
- wide_str[plen - 1] = 0;
- GetTempPathW(plen, wide_str);
- path = utf16_to_utf8_string_alloc(wide_str);
- free(wide_str);
- #endif
- #else
- #if defined ANDROID
- src = override_dir;
- #else
- {
- char *tmpdir = getenv("TMPDIR");
- if (tmpdir)
- src = tmpdir;
- else
- src = "/tmp";
- }
- #endif
- path = (src) ? strcpy_alloc(src) : (char*)calloc(1,1);
- #endif
- return path;
- }
- static bool write_file_with_random_name(char **temp_dll_path,
- const char *retroarch_temp_path, const void* data, ssize_t dataSize)
- {
- unsigned i;
- char number_buf[32];
- bool okay = false;
- const char *prefix = "tmp";
- time_t time_value = time(NULL);
- unsigned number_value = (unsigned)time_value;
- const char *src = path_get_extension(*temp_dll_path);
- char *ext = (src) ? strcpy_alloc(src) : (char*)calloc(1,1);
- int ext_len = (int)strlen(ext);
- if (ext_len > 0)
- {
- strcat_alloc(&ext, ".");
- memmove(ext + 1, ext, ext_len);
- ext[0] = '.';
- ext_len++;
- }
- /* Try up to 30 'random' filenames before giving up */
- for (i = 0; i < 30; i++)
- {
- int number;
- number_value = number_value * 214013 + 2531011;
- number = (number_value >> 14) % 100000;
- snprintf(number_buf, sizeof(number_buf), "%05d", number);
- if (*temp_dll_path)
- free(*temp_dll_path);
- *temp_dll_path = NULL;
- strcat_alloc(temp_dll_path, retroarch_temp_path);
- strcat_alloc(temp_dll_path, prefix);
- strcat_alloc(temp_dll_path, number_buf);
- strcat_alloc(temp_dll_path, ext);
- if (filestream_write_file(*temp_dll_path, data, dataSize))
- {
- okay = true;
- break;
- }
- }
- if (ext)
- free(ext);
- ext = NULL;
- return okay;
- }
- static char *copy_core_to_temp_file(struct rarch_state *p_rarch)
- {
- bool failed = false;
- char *temp_directory = NULL;
- char *retroarch_temp_path = NULL;
- char *temp_dll_path = NULL;
- void *dll_file_data = NULL;
- int64_t dll_file_size = 0;
- const char *core_path = path_get(RARCH_PATH_CORE);
- const char *core_base_name = path_basename(core_path);
- settings_t *settings = p_rarch->configuration_settings;
- const char *dir_libretro = settings->paths.directory_libretro;
- if (strlen(core_base_name) == 0)
- return NULL;
- temp_directory = get_temp_directory_alloc(dir_libretro);
- if (!temp_directory)
- return NULL;
- strcat_alloc(&retroarch_temp_path, temp_directory);
- strcat_alloc(&retroarch_temp_path, PATH_DEFAULT_SLASH());
- strcat_alloc(&retroarch_temp_path, "retroarch_temp");
- strcat_alloc(&retroarch_temp_path, PATH_DEFAULT_SLASH());
- if (!path_mkdir(retroarch_temp_path))
- {
- failed = true;
- goto end;
- }
- if (!filestream_read_file(core_path, &dll_file_data, &dll_file_size))
- {
- failed = true;
- goto end;
- }
- strcat_alloc(&temp_dll_path, retroarch_temp_path);
- strcat_alloc(&temp_dll_path, core_base_name);
- if (!filestream_write_file(temp_dll_path, dll_file_data, dll_file_size))
- {
- /* try other file names */
- if (!write_file_with_random_name(&temp_dll_path,
- retroarch_temp_path, dll_file_data, dll_file_size))
- failed = true;
- }
- end:
- if (temp_directory)
- free(temp_directory);
- if (retroarch_temp_path)
- free(retroarch_temp_path);
- if (dll_file_data)
- free(dll_file_data);
- temp_directory = NULL;
- retroarch_temp_path = NULL;
- dll_file_data = NULL;
- if (!failed)
- return temp_dll_path;
- if (temp_dll_path)
- free(temp_dll_path);
- temp_dll_path = NULL;
- return NULL;
- }
- static bool rarch_environment_secondary_core_hook(
- unsigned cmd, void *data)
- {
- struct rarch_state *p_rarch = &rarch_st;
- bool result = rarch_environment_cb(cmd, data);
- if (p_rarch->has_variable_update)
- {
- if (cmd == RETRO_ENVIRONMENT_GET_VARIABLE_UPDATE)
- {
- bool *bool_p = (bool*)data;
- *bool_p = true;
- p_rarch->has_variable_update = false;
- return true;
- }
- else if (cmd == RETRO_ENVIRONMENT_GET_VARIABLE)
- p_rarch->has_variable_update = false;
- }
- return result;
- }
- static bool secondary_core_create(struct rarch_state *p_rarch)
- {
- unsigned port;
- bool contentless = false;
- bool is_inited = false;
- const enum rarch_core_type
- last_core_type = p_rarch->last_core_type;
- rarch_system_info_t *info = &p_rarch->runloop_system;
- unsigned num_active_users = p_rarch->input_driver_max_users;
- if ( last_core_type != CORE_TYPE_PLAIN ||
- !p_rarch->load_content_info ||
- p_rarch->load_content_info->special)
- return false;
- if (p_rarch->secondary_library_path)
- free(p_rarch->secondary_library_path);
- p_rarch->secondary_library_path = NULL;
- p_rarch->secondary_library_path = copy_core_to_temp_file(p_rarch);
- if (!p_rarch->secondary_library_path)
- return false;
- /* Load Core */
- if (!init_libretro_symbols_custom(p_rarch,
- CORE_TYPE_PLAIN, &p_rarch->secondary_core,
- p_rarch->secondary_library_path,
- &p_rarch->secondary_lib_handle))
- return false;
- p_rarch->secondary_core.symbols_inited = true;
- p_rarch->secondary_core.retro_set_environment(
- rarch_environment_secondary_core_hook);
- #ifdef HAVE_RUNAHEAD
- p_rarch->has_variable_update = true;
- #endif
- p_rarch->secondary_core.retro_init();
- content_get_status(&contentless, &is_inited);
- p_rarch->secondary_core.inited = is_inited;
- /* Load Content */
- /* disabled due to crashes */
- if ( !p_rarch->load_content_info ||
- p_rarch->load_content_info->special)
- return false;
- if ( (p_rarch->load_content_info->content->size > 0) &&
- p_rarch->load_content_info->content->elems[0].data)
- {
- p_rarch->secondary_core.game_loaded = p_rarch->secondary_core.retro_load_game(
- p_rarch->load_content_info->info);
- if (!p_rarch->secondary_core.game_loaded)
- goto error;
- }
- else if (contentless)
- {
- p_rarch->secondary_core.game_loaded = p_rarch->secondary_core.retro_load_game(NULL);
- if (!p_rarch->secondary_core.game_loaded)
- goto error;
- }
- else
- p_rarch->secondary_core.game_loaded = false;
- if (!p_rarch->secondary_core.inited)
- goto error;
- core_set_default_callbacks(&p_rarch->secondary_callbacks);
- p_rarch->secondary_core.retro_set_video_refresh(p_rarch->secondary_callbacks.frame_cb);
- p_rarch->secondary_core.retro_set_audio_sample(p_rarch->secondary_callbacks.sample_cb);
- p_rarch->secondary_core.retro_set_audio_sample_batch(p_rarch->secondary_callbacks.sample_batch_cb);
- p_rarch->secondary_core.retro_set_input_state(p_rarch->secondary_callbacks.state_cb);
- p_rarch->secondary_core.retro_set_input_poll(p_rarch->secondary_callbacks.poll_cb);
- if (info)
- for (port = 0; port < MAX_USERS; port++)
- {
- if (port < info->ports.size)
- {
- unsigned device = (port < num_active_users) ?
- p_rarch->port_map[port] : RETRO_DEVICE_NONE;
- p_rarch->secondary_core.retro_set_controller_port_device(
- port, device);
- }
- }
- clear_controller_port_map(p_rarch);
- return true;
- error:
- secondary_core_destroy(p_rarch);
- return false;
- }
- static void secondary_core_input_poll_null(void) { }
- static bool secondary_core_run_use_last_input(struct rarch_state *p_rarch)
- {
- retro_input_poll_t old_poll_function;
- retro_input_state_t old_input_function;
- if (!secondary_core_ensure_exists(p_rarch))
- {
- secondary_core_destroy(p_rarch);
- return false;
- }
- old_poll_function = p_rarch->secondary_callbacks.poll_cb;
- old_input_function = p_rarch->secondary_callbacks.state_cb;
- p_rarch->secondary_callbacks.poll_cb = secondary_core_input_poll_null;
- p_rarch->secondary_callbacks.state_cb = input_state_get_last;
- p_rarch->secondary_core.retro_set_input_poll(p_rarch->secondary_callbacks.poll_cb);
- p_rarch->secondary_core.retro_set_input_state(p_rarch->secondary_callbacks.state_cb);
- p_rarch->secondary_core.retro_run();
- p_rarch->secondary_callbacks.poll_cb = old_poll_function;
- p_rarch->secondary_callbacks.state_cb = old_input_function;
- p_rarch->secondary_core.retro_set_input_poll(p_rarch->secondary_callbacks.poll_cb);
- p_rarch->secondary_core.retro_set_input_state(p_rarch->secondary_callbacks.state_cb);
- return true;
- }
- #else
- static void secondary_core_destroy(struct rarch_state *p_rarch) { }
- static void remember_controller_port_device(
- struct rarch_state *p_rarch,
- long port, long device) { }
- static void clear_controller_port_map(struct rarch_state *p_rarch) { }
- #endif
- #endif
- /* BLUETOOTH DRIVER */
- /**
- * config_get_bluetooth_driver_options:
- *
- * Get an enumerated list of all bluetooth driver names,
- * separated by '|'.
- *
- * Returns: string listing of all bluetooth driver names,
- * separated by '|'.
- **/
- const char* config_get_bluetooth_driver_options(void)
- {
- return char_list_new_special(STRING_LIST_BLUETOOTH_DRIVERS, NULL);
- }
- void driver_bluetooth_scan(void)
- {
- struct rarch_state *p_rarch = &rarch_st;
- if ( (p_rarch->bluetooth_driver_active) &&
- (p_rarch->bluetooth_driver->scan) )
- p_rarch->bluetooth_driver->scan(p_rarch->bluetooth_data);
- }
- void driver_bluetooth_get_devices(struct string_list* devices)
- {
- struct rarch_state *p_rarch = &rarch_st;
- if ( (p_rarch->bluetooth_driver_active) &&
- (p_rarch->bluetooth_driver->get_devices) )
- p_rarch->bluetooth_driver->get_devices(p_rarch->bluetooth_data, devices);
- }
- bool driver_bluetooth_device_is_connected(unsigned i)
- {
- struct rarch_state *p_rarch = &rarch_st;
- if ( (p_rarch->bluetooth_driver_active) &&
- (p_rarch->bluetooth_driver->device_is_connected) )
- return p_rarch->bluetooth_driver->device_is_connected(p_rarch->bluetooth_data, i);
- return false;
- }
- void driver_bluetooth_device_get_sublabel(char *s, unsigned i, size_t len)
- {
- struct rarch_state *p_rarch = &rarch_st;
- if ( (p_rarch->bluetooth_driver_active) &&
- (p_rarch->bluetooth_driver->device_get_sublabel) )
- p_rarch->bluetooth_driver->device_get_sublabel(p_rarch->bluetooth_data, s, i, len);
- }
- bool driver_bluetooth_connect_device(unsigned i)
- {
- struct rarch_state *p_rarch = &rarch_st;
- if (p_rarch->bluetooth_driver_active)
- return p_rarch->bluetooth_driver->connect_device(p_rarch->bluetooth_data, i);
- return false;
- }
- bool bluetooth_driver_ctl(enum rarch_bluetooth_ctl_state state, void *data)
- {
- struct rarch_state *p_rarch = &rarch_st;
- settings_t *settings = p_rarch->configuration_settings;
- switch (state)
- {
- case RARCH_BLUETOOTH_CTL_DESTROY:
- p_rarch->bluetooth_driver = NULL;
- p_rarch->bluetooth_data = NULL;
- p_rarch->bluetooth_driver_active = false;
- break;
- case RARCH_BLUETOOTH_CTL_FIND_DRIVER:
- {
- int i = (int)driver_find_index(
- "bluetooth_driver",
- settings->arrays.bluetooth_driver);
- if (i >= 0)
- p_rarch->bluetooth_driver = (const bluetooth_driver_t*)bluetooth_drivers[i];
- else
- {
- if (verbosity_is_enabled())
- {
- unsigned d;
- RARCH_ERR("Couldn't find any bluetooth driver named \"%s\"\n",
- settings->arrays.bluetooth_driver);
- RARCH_LOG_OUTPUT("Available bluetooth drivers are:\n");
- for (d = 0; bluetooth_drivers[d]; d++)
- RARCH_LOG_OUTPUT("\t%s\n", bluetooth_drivers[d]->ident);
- RARCH_WARN("Going to default to first bluetooth driver...\n");
- }
- p_rarch->bluetooth_driver = (const bluetooth_driver_t*)bluetooth_drivers[0];
- if (!p_rarch->bluetooth_driver)
- retroarch_fail(1, "find_bluetooth_driver()");
- }
- }
- break;
- case RARCH_BLUETOOTH_CTL_DEINIT:
- if (p_rarch->bluetooth_data && p_rarch->bluetooth_driver)
- {
- if (p_rarch->bluetooth_driver->free)
- p_rarch->bluetooth_driver->free(p_rarch->bluetooth_data);
- }
- p_rarch->bluetooth_data = NULL;
- p_rarch->bluetooth_driver_active = false;
- break;
- case RARCH_BLUETOOTH_CTL_INIT:
- /* Resource leaks will follow if bluetooth is initialized twice. */
- if (p_rarch->bluetooth_data)
- return false;
- bluetooth_driver_ctl(RARCH_BLUETOOTH_CTL_FIND_DRIVER, NULL);
- if (p_rarch->bluetooth_driver && p_rarch->bluetooth_driver->init)
- {
- p_rarch->bluetooth_driver_active = true;
- p_rarch->bluetooth_data = p_rarch->bluetooth_driver->init();
- if (!p_rarch->bluetooth_data)
- {
- RARCH_ERR("Failed to initialize bluetooth driver. Will continue without bluetooth.\n");
- p_rarch->bluetooth_driver_active = false;
- }
- } else {
- p_rarch->bluetooth_driver_active = false;
- }
- break;
- default:
- break;
- }
- return false;
- }
- /* WIFI DRIVER */
- /**
- * config_get_wifi_driver_options:
- *
- * Get an enumerated list of all wifi driver names,
- * separated by '|'.
- *
- * Returns: string listing of all wifi driver names,
- * separated by '|'.
- **/
- const char* config_get_wifi_driver_options(void)
- {
- return char_list_new_special(STRING_LIST_WIFI_DRIVERS, NULL);
- }
- void driver_wifi_scan(void)
- {
- struct rarch_state *p_rarch = &rarch_st;
- p_rarch->wifi_driver->scan(p_rarch->wifi_data);
- }
- bool driver_wifi_enable(bool enabled)
- {
- struct rarch_state *p_rarch = &rarch_st;
- return p_rarch->wifi_driver->enable(p_rarch->wifi_data, enabled);
- }
- bool driver_wifi_connection_info(wifi_network_info_t *netinfo)
- {
- struct rarch_state *p_rarch = &rarch_st;
- return p_rarch->wifi_driver->connection_info(p_rarch->wifi_data, netinfo);
- }
- wifi_network_scan_t* driver_wifi_get_ssids()
- {
- struct rarch_state *p_rarch = &rarch_st;
- return p_rarch->wifi_driver->get_ssids(p_rarch->wifi_data);
- }
- bool driver_wifi_ssid_is_online(unsigned i)
- {
- struct rarch_state *p_rarch = &rarch_st;
- return p_rarch->wifi_driver->ssid_is_online(p_rarch->wifi_data, i);
- }
- bool driver_wifi_connect_ssid(const wifi_network_info_t* net)
- {
- struct rarch_state *p_rarch = &rarch_st;
- return p_rarch->wifi_driver->connect_ssid(p_rarch->wifi_data, net);
- }
- bool driver_wifi_disconnect_ssid(const wifi_network_info_t* net)
- {
- struct rarch_state *p_rarch = &rarch_st;
- return p_rarch->wifi_driver->disconnect_ssid(p_rarch->wifi_data, net);
- }
- void driver_wifi_tether_start_stop(bool start, char* configfile)
- {
- struct rarch_state *p_rarch = &rarch_st;
- p_rarch->wifi_driver->tether_start_stop(p_rarch->wifi_data, start, configfile);
- }
- bool wifi_driver_ctl(enum rarch_wifi_ctl_state state, void *data)
- {
- struct rarch_state *p_rarch = &rarch_st;
- settings_t *settings = p_rarch->configuration_settings;
- switch (state)
- {
- case RARCH_WIFI_CTL_DESTROY:
- p_rarch->wifi_driver_active = false;
- p_rarch->wifi_driver = NULL;
- p_rarch->wifi_data = NULL;
- break;
- case RARCH_WIFI_CTL_SET_ACTIVE:
- p_rarch->wifi_driver_active = true;
- break;
- case RARCH_WIFI_CTL_FIND_DRIVER:
- {
- int i = (int)driver_find_index(
- "wifi_driver",
- settings->arrays.wifi_driver);
- if (i >= 0)
- p_rarch->wifi_driver = (const wifi_driver_t*)wifi_drivers[i];
- else
- {
- if (verbosity_is_enabled())
- {
- unsigned d;
- RARCH_ERR("Couldn't find any wifi driver named \"%s\"\n",
- settings->arrays.wifi_driver);
- RARCH_LOG_OUTPUT("Available wifi drivers are:\n");
- for (d = 0; wifi_drivers[d]; d++)
- RARCH_LOG_OUTPUT("\t%s\n", wifi_drivers[d]->ident);
- RARCH_WARN("Going to default to first wifi driver...\n");
- }
- p_rarch->wifi_driver = (const wifi_driver_t*)wifi_drivers[0];
- if (!p_rarch->wifi_driver)
- retroarch_fail(1, "find_wifi_driver()");
- }
- }
- break;
- case RARCH_WIFI_CTL_UNSET_ACTIVE:
- p_rarch->wifi_driver_active = false;
- break;
- case RARCH_WIFI_CTL_IS_ACTIVE:
- return p_rarch->wifi_driver_active;
- case RARCH_WIFI_CTL_DEINIT:
- if (p_rarch->wifi_data && p_rarch->wifi_driver)
- {
- if (p_rarch->wifi_driver->free)
- p_rarch->wifi_driver->free(p_rarch->wifi_data);
- }
- p_rarch->wifi_data = NULL;
- break;
- case RARCH_WIFI_CTL_STOP:
- if ( p_rarch->wifi_driver
- && p_rarch->wifi_driver->stop
- && p_rarch->wifi_data)
- p_rarch->wifi_driver->stop(p_rarch->wifi_data);
- break;
- case RARCH_WIFI_CTL_START:
- if ( p_rarch->wifi_driver
- && p_rarch->wifi_data
- && p_rarch->wifi_driver->start)
- {
- bool wifi_allow = settings->bools.wifi_allow;
- if (wifi_allow)
- return p_rarch->wifi_driver->start(p_rarch->wifi_data);
- }
- return false;
- case RARCH_WIFI_CTL_INIT:
- /* Resource leaks will follow if wifi is initialized twice. */
- if (p_rarch->wifi_data)
- return false;
- wifi_driver_ctl(RARCH_WIFI_CTL_FIND_DRIVER, NULL);
- if (p_rarch->wifi_driver && p_rarch->wifi_driver->init)
- {
- p_rarch->wifi_data = p_rarch->wifi_driver->init();
- if (p_rarch->wifi_data)
- {
- p_rarch->wifi_driver->enable(p_rarch->wifi_data,
- settings->bools.wifi_enabled);
- }
- else
- {
- RARCH_ERR("Failed to initialize wifi driver. Will continue without wifi.\n");
- wifi_driver_ctl(RARCH_WIFI_CTL_UNSET_ACTIVE, NULL);
- }
- }
- break;
- default:
- break;
- }
- return false;
- }
- /* UI COMPANION */
- void ui_companion_set_foreground(unsigned enable)
- {
- struct rarch_state *p_rarch = &rarch_st;
- p_rarch->main_ui_companion_is_on_foreground = enable;
- }
- bool ui_companion_is_on_foreground(void)
- {
- struct rarch_state *p_rarch = &rarch_st;
- return p_rarch->main_ui_companion_is_on_foreground;
- }
- void ui_companion_event_command(enum event_command action)
- {
- struct rarch_state *p_rarch = &rarch_st;
- #ifdef HAVE_QT
- bool qt_is_inited = p_rarch->qt_is_inited;
- #endif
- const ui_companion_driver_t *ui = p_rarch->ui_companion;
- if (ui && ui->event_command)
- ui->event_command(p_rarch->ui_companion_data, action);
- #ifdef HAVE_QT
- if (ui_companion_qt.toggle && qt_is_inited)
- ui_companion_qt.event_command(
- p_rarch->ui_companion_qt_data, action);
- #endif
- }
- static void ui_companion_driver_deinit(struct rarch_state *p_rarch)
- {
- #ifdef HAVE_QT
- bool qt_is_inited = p_rarch->qt_is_inited;
- #endif
- const ui_companion_driver_t *ui = p_rarch->ui_companion;
- if (!ui)
- return;
- if (ui->deinit)
- ui->deinit(p_rarch->ui_companion_data);
- #ifdef HAVE_QT
- if (qt_is_inited)
- {
- ui_companion_qt.deinit(p_rarch->ui_companion_qt_data);
- p_rarch->ui_companion_qt_data = NULL;
- }
- #endif
- p_rarch->ui_companion_data = NULL;
- }
- static void ui_companion_driver_init_first(
- settings_t *settings,
- struct rarch_state *p_rarch)
- {
- #ifdef HAVE_QT
- bool desktop_menu_enable = settings->bools.desktop_menu_enable;
- bool ui_companion_toggle = settings->bools.ui_companion_toggle;
- if (desktop_menu_enable && ui_companion_toggle)
- {
- p_rarch->ui_companion_qt_data = ui_companion_qt.init();
- p_rarch->qt_is_inited = true;
- }
- #endif
- p_rarch->ui_companion = (ui_companion_driver_t*)ui_companion_drivers[0];
- if (p_rarch->ui_companion)
- {
- unsigned ui_companion_start_on_boot =
- settings->bools.ui_companion_start_on_boot;
- if (ui_companion_start_on_boot)
- {
- if (p_rarch->ui_companion->init)
- p_rarch->ui_companion_data = p_rarch->ui_companion->init();
- ui_companion_driver_toggle(settings, p_rarch, false);
- }
- }
- }
- static void ui_companion_driver_toggle(
- settings_t *settings,
- struct rarch_state *p_rarch,
- bool force)
- {
- #ifdef HAVE_QT
- bool desktop_menu_enable = settings->bools.desktop_menu_enable;
- bool ui_companion_toggle = settings->bools.ui_companion_toggle;
- #endif
- if (p_rarch->ui_companion && p_rarch->ui_companion->toggle)
- p_rarch->ui_companion->toggle(p_rarch->ui_companion_data, false);
- #ifdef HAVE_QT
- if (desktop_menu_enable)
- {
- if ((ui_companion_toggle || force) && !p_rarch->qt_is_inited)
- {
- p_rarch->ui_companion_qt_data = ui_companion_qt.init();
- p_rarch->qt_is_inited = true;
- }
- if (ui_companion_qt.toggle && p_rarch->qt_is_inited)
- ui_companion_qt.toggle(p_rarch->ui_companion_qt_data, force);
- }
- #endif
- }
- void ui_companion_driver_notify_refresh(void)
- {
- struct rarch_state *p_rarch = &rarch_st;
- const ui_companion_driver_t *ui = p_rarch->ui_companion;
- #ifdef HAVE_QT
- settings_t *settings = p_rarch->configuration_settings;
- bool desktop_menu_enable = settings->bools.desktop_menu_enable;
- bool qt_is_inited = p_rarch->qt_is_inited;
- #endif
- if (!ui)
- return;
- if (ui->notify_refresh)
- ui->notify_refresh(p_rarch->ui_companion_data);
- #ifdef HAVE_QT
- if (desktop_menu_enable)
- if (ui_companion_qt.notify_refresh && qt_is_inited)
- ui_companion_qt.notify_refresh(p_rarch->ui_companion_qt_data);
- #endif
- }
- void ui_companion_driver_notify_list_loaded(
- file_list_t *list, file_list_t *menu_list)
- {
- struct rarch_state *p_rarch = &rarch_st;
- const ui_companion_driver_t *ui = p_rarch->ui_companion;
- if (ui && ui->notify_list_loaded)
- ui->notify_list_loaded(p_rarch->ui_companion_data, list, menu_list);
- }
- void ui_companion_driver_notify_content_loaded(void)
- {
- struct rarch_state *p_rarch = &rarch_st;
- const ui_companion_driver_t *ui = p_rarch->ui_companion;
- if (ui && ui->notify_content_loaded)
- ui->notify_content_loaded(p_rarch->ui_companion_data);
- }
- void ui_companion_driver_free(void)
- {
- struct rarch_state *p_rarch = &rarch_st;
- p_rarch->ui_companion = NULL;
- }
- const ui_msg_window_t *ui_companion_driver_get_msg_window_ptr(void)
- {
- struct rarch_state *p_rarch = &rarch_st;
- const ui_companion_driver_t *ui = p_rarch->ui_companion;
- if (!ui)
- return NULL;
- return ui->msg_window;
- }
- const ui_window_t *ui_companion_driver_get_window_ptr(void)
- {
- struct rarch_state *p_rarch = &rarch_st;
- const ui_companion_driver_t *ui = p_rarch->ui_companion;
- if (!ui)
- return NULL;
- return ui->window;
- }
- const ui_browser_window_t *ui_companion_driver_get_browser_window_ptr(void)
- {
- struct rarch_state *p_rarch = &rarch_st;
- const ui_companion_driver_t *ui = p_rarch->ui_companion;
- if (!ui)
- return NULL;
- return ui->browser_window;
- }
- static void ui_companion_driver_msg_queue_push(
- struct rarch_state *p_rarch,
- const char *msg, unsigned priority, unsigned duration, bool flush)
- {
- const ui_companion_driver_t *ui = p_rarch->ui_companion;
- if (ui && ui->msg_queue_push)
- ui->msg_queue_push(p_rarch->ui_companion_data, msg, priority, duration, flush);
- #ifdef HAVE_QT
- {
- settings_t *settings = p_rarch->configuration_settings;
- bool qt_is_inited = p_rarch->qt_is_inited;
- bool desktop_menu_enable = settings->bools.desktop_menu_enable;
- if (desktop_menu_enable)
- if (ui_companion_qt.msg_queue_push && qt_is_inited)
- ui_companion_qt.msg_queue_push(
- p_rarch->ui_companion_qt_data,
- msg, priority, duration, flush);
- }
- #endif
- }
- void *ui_companion_driver_get_main_window(void)
- {
- struct rarch_state
- *p_rarch = &rarch_st;
- const ui_companion_driver_t *ui = p_rarch->ui_companion;
- if (!ui || !ui->get_main_window)
- return NULL;
- return ui->get_main_window(p_rarch->ui_companion_data);
- }
- const char *ui_companion_driver_get_ident(void)
- {
- struct rarch_state
- *p_rarch = &rarch_st;
- const ui_companion_driver_t *ui = p_rarch->ui_companion;
- if (!ui)
- return "null";
- return ui->ident;
- }
- void ui_companion_driver_log_msg(const char *msg)
- {
- #ifdef HAVE_QT
- struct rarch_state *p_rarch = &rarch_st;
- settings_t *settings = p_rarch->configuration_settings;
- bool qt_is_inited = p_rarch->qt_is_inited;
- bool desktop_menu_enable = settings->bools.desktop_menu_enable;
- bool window_is_active = p_rarch->ui_companion_qt_data && qt_is_inited
- && ui_companion_qt.is_active(p_rarch->ui_companion_qt_data);
- if (desktop_menu_enable)
- if (window_is_active)
- ui_companion_qt.log_msg(p_rarch->ui_companion_qt_data, msg);
- #endif
- }
- /* RECORDING */
- /**
- * config_get_record_driver_options:
- *
- * Get an enumerated list of all record driver names, separated by '|'.
- *
- * Returns: string listing of all record driver names, separated by '|'.
- **/
- const char* config_get_record_driver_options(void)
- {
- return char_list_new_special(STRING_LIST_RECORD_DRIVERS, NULL);
- }
- #if 0
- /* TODO/FIXME - not used apparently */
- static void find_record_driver(void)
- {
- settings_t *settings = p_rarch->configuration_settings;
- int i = (int)driver_find_index(
- "record_driver",
- settings->arrays.record_driver);
- if (i >= 0)
- p_rarch->recording_driver = (const record_driver_t*)record_drivers[i];
- else
- {
- if (verbosity_is_enabled())
- {
- unsigned d;
- RARCH_ERR("[recording] Couldn't find any record driver named \"%s\"\n",
- settings->arrays.record_driver);
- RARCH_LOG_OUTPUT("Available record drivers are:\n");
- for (d = 0; record_drivers[d]; d++)
- RARCH_LOG_OUTPUT("\t%s\n", record_drivers[d].ident);
- RARCH_WARN("[recording] Going to default to first record driver...\n");
- }
- p_rarch->recording_driver = (const record_driver_t*)record_drivers[0];
- if (!p_rarch->recording_driver)
- retroarch_fail(1, "find_record_driver()");
- }
- }
- /**
- * ffemu_find_backend:
- * @ident : Identifier of driver to find.
- *
- * Finds a recording driver with the name @ident.
- *
- * Returns: recording driver handle if successful, otherwise
- * NULL.
- **/
- static const record_driver_t *ffemu_find_backend(const char *ident)
- {
- unsigned i;
- for (i = 0; record_drivers[i]; i++)
- {
- if (string_is_equal(record_drivers[i]->ident, ident))
- return record_drivers[i];
- }
- return NULL;
- }
- static void recording_driver_free_state(void)
- {
- /* TODO/FIXME - this is not being called anywhere */
- p_rarch->recording_gpu_width = 0;
- p_rarch->recording_gpu_height = 0;
- p_rarch->recording_width = 0;
- p_rarch->recording_height = 0;
- }
- #endif
- /**
- * gfx_ctx_init_first:
- * @backend : Recording backend handle.
- * @data : Recording data handle.
- * @params : Recording info parameters.
- *
- * Finds first suitable recording context driver and initializes.
- *
- * Returns: true (1) if successful, otherwise false (0).
- **/
- static bool record_driver_init_first(
- const record_driver_t **backend, void **data,
- const struct record_params *params)
- {
- unsigned i;
- for (i = 0; record_drivers[i]; i++)
- {
- void *handle = record_drivers[i]->init(params);
- if (!handle)
- continue;
- *backend = record_drivers[i];
- *data = handle;
- return true;
- }
- return false;
- }
- static void recording_dump_frame(
- struct rarch_state *p_rarch,
- const void *data, unsigned width,
- unsigned height, size_t pitch, bool is_idle)
- {
- struct record_video_data ffemu_data;
- ffemu_data.data = data;
- ffemu_data.width = width;
- ffemu_data.height = height;
- ffemu_data.pitch = (int)pitch;
- ffemu_data.is_dupe = false;
- if (p_rarch->video_driver_record_gpu_buffer)
- {
- struct video_viewport vp;
- vp.x = 0;
- vp.y = 0;
- vp.width = 0;
- vp.height = 0;
- vp.full_width = 0;
- vp.full_height = 0;
- video_driver_get_viewport_info(&vp);
- if (!vp.width || !vp.height)
- {
- RARCH_WARN("[recording] %s \n",
- msg_hash_to_str(MSG_VIEWPORT_SIZE_CALCULATION_FAILED));
- video_driver_gpu_record_deinit(p_rarch);
- recording_dump_frame(p_rarch,
- data, width, height, pitch, is_idle);
- return;
- }
- /* User has resized. We kinda have a problem now. */
- if ( vp.width != p_rarch->recording_gpu_width ||
- vp.height != p_rarch->recording_gpu_height)
- {
- RARCH_WARN("[recording] %s\n",
- msg_hash_to_str(MSG_RECORDING_TERMINATED_DUE_TO_RESIZE));
- runloop_msg_queue_push(
- msg_hash_to_str(MSG_RECORDING_TERMINATED_DUE_TO_RESIZE),
- 1, 180, true,
- NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO);
- command_event(CMD_EVENT_RECORD_DEINIT, NULL);
- return;
- }
- /* Big bottleneck.
- * Since we might need to do read-backs asynchronously,
- * it might take 3-4 times before this returns true. */
- if (!video_driver_read_viewport(p_rarch->video_driver_record_gpu_buffer, is_idle))
- return;
- ffemu_data.pitch = (int)(p_rarch->recording_gpu_width * 3);
- ffemu_data.width = (unsigned)p_rarch->recording_gpu_width;
- ffemu_data.height = (unsigned)p_rarch->recording_gpu_height;
- ffemu_data.data = p_rarch->video_driver_record_gpu_buffer + (ffemu_data.height - 1) * ffemu_data.pitch;
- ffemu_data.pitch = -ffemu_data.pitch;
- }
- else
- ffemu_data.is_dupe = !data;
- p_rarch->recording_driver->push_video(p_rarch->recording_data, &ffemu_data);
- }
- static bool recording_deinit(struct rarch_state *p_rarch)
- {
- if (!p_rarch->recording_data || !p_rarch->recording_driver)
- return false;
- if (p_rarch->recording_driver->finalize)
- p_rarch->recording_driver->finalize(p_rarch->recording_data);
- if (p_rarch->recording_driver->free)
- p_rarch->recording_driver->free(p_rarch->recording_data);
- p_rarch->recording_data = NULL;
- p_rarch->recording_driver = NULL;
- video_driver_gpu_record_deinit(p_rarch);
- return true;
- }
- bool recording_is_enabled(void)
- {
- struct rarch_state *p_rarch = &rarch_st;
- return p_rarch->recording_enable;
- }
- bool streaming_is_enabled(void)
- {
- struct rarch_state *p_rarch = &rarch_st;
- return p_rarch->streaming_enable;
- }
- void streaming_set_state(bool state)
- {
- struct rarch_state *p_rarch = &rarch_st;
- p_rarch->streaming_enable = state;
- }
- static void video_driver_gpu_record_deinit(struct rarch_state *p_rarch)
- {
- if (p_rarch->video_driver_record_gpu_buffer)
- free(p_rarch->video_driver_record_gpu_buffer);
- p_rarch->video_driver_record_gpu_buffer = NULL;
- }
- /**
- * recording_init:
- *
- * Initializes recording.
- *
- * Returns: true (1) if successful, otherwise false (0).
- **/
- static bool recording_init(
- settings_t *settings,
- struct rarch_state *p_rarch)
- {
- char output[PATH_MAX_LENGTH];
- char buf[PATH_MAX_LENGTH];
- struct record_params params = {0};
- struct retro_system_av_info *av_info = &p_rarch->video_driver_av_info;
- global_t *global = &p_rarch->g_extern;
- bool video_gpu_record = settings->bools.video_gpu_record;
- bool video_force_aspect = settings->bools.video_force_aspect;
- const enum rarch_core_type
- current_core_type = p_rarch->current_core_type;
- const enum retro_pixel_format
- video_driver_pix_fmt = p_rarch->video_driver_pix_fmt;
- bool recording_enable = p_rarch->recording_enable;
- if (!recording_enable)
- return false;
- output[0] = '\0';
- if (current_core_type == CORE_TYPE_DUMMY)
- {
- RARCH_WARN("[recording] %s\n",
- msg_hash_to_str(MSG_USING_LIBRETRO_DUMMY_CORE_RECORDING_SKIPPED));
- return false;
- }
- if (!video_gpu_record && video_driver_is_hw_context())
- {
- RARCH_WARN("[recording] %s.\n",
- msg_hash_to_str(MSG_HW_RENDERED_MUST_USE_POSTSHADED_RECORDING));
- return false;
- }
- RARCH_LOG("[recording] %s: FPS: %.4f, Sample rate: %.4f\n",
- msg_hash_to_str(MSG_CUSTOM_TIMING_GIVEN),
- (float)av_info->timing.fps,
- (float)av_info->timing.sample_rate);
- if (!string_is_empty(global->record.path))
- strlcpy(output, global->record.path, sizeof(output));
- else
- {
- const char *stream_url = settings->paths.path_stream_url;
- unsigned video_record_quality = settings->uints.video_record_quality;
- unsigned video_stream_port = settings->uints.video_stream_port;
- if (p_rarch->streaming_enable)
- if (!string_is_empty(stream_url))
- strlcpy(output, stream_url, sizeof(output));
- else
- /* Fallback, stream locally to 127.0.0.1 */
- snprintf(output, sizeof(output), "udp://127.0.0.1:%u",
- video_stream_port);
- else
- {
- const char *game_name = path_basename(path_get(RARCH_PATH_BASENAME));
- /* Fallback to core name if started without content */
- if (string_is_empty(game_name))
- game_name = p_rarch->runloop_system.info.library_name;
- if (video_record_quality < RECORD_CONFIG_TYPE_RECORDING_WEBM_FAST)
- {
- fill_str_dated_filename(buf, game_name,
- "mkv", sizeof(buf));
- fill_pathname_join(output, global->record.output_dir, buf, sizeof(output));
- }
- else if (video_record_quality >= RECORD_CONFIG_TYPE_RECORDING_WEBM_FAST
- && video_record_quality < RECORD_CONFIG_TYPE_RECORDING_GIF)
- {
- fill_str_dated_filename(buf, game_name,
- "webm", sizeof(buf));
- fill_pathname_join(output, global->record.output_dir, buf, sizeof(output));
- }
- else if (video_record_quality >= RECORD_CONFIG_TYPE_RECORDING_GIF
- && video_record_quality < RECORD_CONFIG_TYPE_RECORDING_APNG)
- {
- fill_str_dated_filename(buf, game_name,
- "gif", sizeof(buf));
- fill_pathname_join(output, global->record.output_dir, buf, sizeof(output));
- }
- else
- {
- fill_str_dated_filename(buf, game_name,
- "png", sizeof(buf));
- fill_pathname_join(output, global->record.output_dir, buf, sizeof(output));
- }
- }
- }
- params.audio_resampler = settings->arrays.audio_resampler;
- params.video_gpu_record = settings->bools.video_gpu_record;
- params.video_record_scale_factor = settings->uints.video_record_scale_factor;
- params.video_stream_scale_factor = settings->uints.video_stream_scale_factor;
- params.video_record_threads = settings->uints.video_record_threads;
- params.streaming_mode = settings->uints.streaming_mode;
- params.out_width = av_info->geometry.base_width;
- params.out_height = av_info->geometry.base_height;
- params.fb_width = av_info->geometry.max_width;
- params.fb_height = av_info->geometry.max_height;
- params.channels = 2;
- params.filename = output;
- params.fps = av_info->timing.fps;
- params.samplerate = av_info->timing.sample_rate;
- params.pix_fmt =
- (video_driver_pix_fmt == RETRO_PIXEL_FORMAT_XRGB8888)
- ? FFEMU_PIX_ARGB8888
- : FFEMU_PIX_RGB565;
- params.config = NULL;
- if (!string_is_empty(global->record.config))
- params.config = global->record.config;
- else
- {
- if (p_rarch->streaming_enable)
- {
- params.config = settings->paths.path_stream_config;
- params.preset = (enum record_config_type)
- settings->uints.video_stream_quality;
- }
- else
- {
- params.config = settings->paths.path_record_config;
- params.preset = (enum record_config_type)
- settings->uints.video_record_quality;
- }
- }
- if (settings->bools.video_gpu_record
- && p_rarch->current_video->read_viewport)
- {
- unsigned gpu_size;
- struct video_viewport vp;
- vp.x = 0;
- vp.y = 0;
- vp.width = 0;
- vp.height = 0;
- vp.full_width = 0;
- vp.full_height = 0;
- video_driver_get_viewport_info(&vp);
- if (!vp.width || !vp.height)
- {
- RARCH_ERR("[recording] Failed to get viewport information from video driver. "
- "Cannot start recording ...\n");
- return false;
- }
- params.out_width = vp.width;
- params.out_height = vp.height;
- params.fb_width = next_pow2(vp.width);
- params.fb_height = next_pow2(vp.height);
- if (video_force_aspect &&
- (p_rarch->video_driver_aspect_ratio > 0.0f))
- params.aspect_ratio = p_rarch->video_driver_aspect_ratio;
- else
- params.aspect_ratio = (float)vp.width / vp.height;
- params.pix_fmt = FFEMU_PIX_BGR24;
- p_rarch->recording_gpu_width = vp.width;
- p_rarch->recording_gpu_height = vp.height;
- RARCH_LOG("[recording] %s %u x %u\n", msg_hash_to_str(MSG_DETECTED_VIEWPORT_OF),
- vp.width, vp.height);
- gpu_size = vp.width * vp.height * 3;
- if (!(p_rarch->video_driver_record_gpu_buffer = (uint8_t*)malloc(gpu_size)))
- return false;
- }
- else
- {
- if (p_rarch->recording_width || p_rarch->recording_height)
- {
- params.out_width = p_rarch->recording_width;
- params.out_height = p_rarch->recording_height;
- }
- if (video_force_aspect &&
- (p_rarch->video_driver_aspect_ratio > 0.0f))
- params.aspect_ratio = p_rarch->video_driver_aspect_ratio;
- else
- params.aspect_ratio = (float)params.out_width / params.out_height;
- #ifdef HAVE_VIDEO_FILTER
- if (settings->bools.video_post_filter_record
- && !!p_rarch->video_driver_state_filter)
- {
- unsigned max_width = 0;
- unsigned max_height = 0;
- params.pix_fmt = FFEMU_PIX_RGB565;
- if (p_rarch->video_driver_state_out_rgb32)
- params.pix_fmt = FFEMU_PIX_ARGB8888;
- rarch_softfilter_get_max_output_size(
- p_rarch->video_driver_state_filter,
- &max_width, &max_height);
- params.fb_width = next_pow2(max_width);
- params.fb_height = next_pow2(max_height);
- }
- #endif
- }
- RARCH_LOG("[recording] %s %s @ %ux%u. (FB size: %ux%u pix_fmt: %u)\n",
- msg_hash_to_str(MSG_RECORDING_TO),
- output,
- params.out_width, params.out_height,
- params.fb_width, params.fb_height,
- (unsigned)params.pix_fmt);
- if (!record_driver_init_first(
- &p_rarch->recording_driver, &p_rarch->recording_data, ¶ms))
- {
- RARCH_ERR("[recording] %s\n",
- msg_hash_to_str(MSG_FAILED_TO_START_RECORDING));
- video_driver_gpu_record_deinit(p_rarch);
- return false;
- }
- return true;
- }
- void recording_driver_update_streaming_url(void)
- {
- struct rarch_state *p_rarch = &rarch_st;
- settings_t *settings = p_rarch->configuration_settings;
- const char *youtube_url = "rtmp://a.rtmp.youtube.com/live2/";
- const char *twitch_url = "rtmp://live.twitch.tv/app/";
- const char *facebook_url = "rtmps://live-api-s.facebook.com:443/rtmp/";
- if (!settings)
- return;
- switch (settings->uints.streaming_mode)
- {
- case STREAMING_MODE_TWITCH:
- if (!string_is_empty(settings->arrays.twitch_stream_key))
- {
- strlcpy(settings->paths.path_stream_url,
- twitch_url,
- sizeof(settings->paths.path_stream_url));
- strlcat(settings->paths.path_stream_url,
- settings->arrays.twitch_stream_key,
- sizeof(settings->paths.path_stream_url));
- }
- break;
- case STREAMING_MODE_YOUTUBE:
- if (!string_is_empty(settings->arrays.youtube_stream_key))
- {
- strlcpy(settings->paths.path_stream_url,
- youtube_url,
- sizeof(settings->paths.path_stream_url));
- strlcat(settings->paths.path_stream_url,
- settings->arrays.youtube_stream_key,
- sizeof(settings->paths.path_stream_url));
- }
- break;
- case STREAMING_MODE_LOCAL:
- /* TODO: figure out default interface and bind to that instead */
- snprintf(settings->paths.path_stream_url, sizeof(settings->paths.path_stream_url),
- "udp://%s:%u", "127.0.0.1", settings->uints.video_stream_port);
- break;
- case STREAMING_MODE_CUSTOM:
- default:
- /* Do nothing, let the user input the URL */
- break;
- case STREAMING_MODE_FACEBOOK:
- if (!string_is_empty(settings->arrays.facebook_stream_key))
- {
- strlcpy(settings->paths.path_stream_url,
- facebook_url,
- sizeof(settings->paths.path_stream_url));
- strlcat(settings->paths.path_stream_url,
- settings->arrays.facebook_stream_key,
- sizeof(settings->paths.path_stream_url));
- }
- break;
- }
- }
- #ifdef HAVE_BSV_MOVIE
- /* BSV MOVIE */
- static bool bsv_movie_init_playback(
- bsv_movie_t *handle, const char *path)
- {
- uint32_t state_size = 0;
- uint32_t content_crc = 0;
- uint32_t header[4] = {0};
- intfstream_t *file = intfstream_open_file(path,
- RETRO_VFS_FILE_ACCESS_READ,
- RETRO_VFS_FILE_ACCESS_HINT_NONE);
- if (!file)
- {
- RARCH_ERR("Could not open BSV file for playback, path : \"%s\".\n", path);
- return false;
- }
- handle->file = file;
- handle->playback = true;
- intfstream_read(handle->file, header, sizeof(uint32_t) * 4);
- /* Compatibility with old implementation that
- * used incorrect documentation. */
- if (swap_if_little32(header[MAGIC_INDEX]) != BSV_MAGIC
- && swap_if_big32(header[MAGIC_INDEX]) != BSV_MAGIC)
- {
- RARCH_ERR("%s\n", msg_hash_to_str(MSG_MOVIE_FILE_IS_NOT_A_VALID_BSV1_FILE));
- return false;
- }
- content_crc = content_get_crc();
- if (content_crc != 0)
- if (swap_if_big32(header[CRC_INDEX]) != content_crc)
- RARCH_WARN("%s.\n", msg_hash_to_str(MSG_CRC32_CHECKSUM_MISMATCH));
- state_size = swap_if_big32(header[STATE_SIZE_INDEX]);
- #if 0
- RARCH_ERR("----- debug %u -----\n", header[0]);
- RARCH_ERR("----- debug %u -----\n", header[1]);
- RARCH_ERR("----- debug %u -----\n", header[2]);
- RARCH_ERR("----- debug %u -----\n", header[3]);
- #endif
- if (state_size)
- {
- retro_ctx_size_info_t info;
- retro_ctx_serialize_info_t serial_info;
- uint8_t *buf = (uint8_t*)malloc(state_size);
- if (!buf)
- return false;
- handle->state = buf;
- handle->state_size = state_size;
- if (intfstream_read(handle->file,
- handle->state, state_size) != state_size)
- {
- RARCH_ERR("%s\n", msg_hash_to_str(MSG_COULD_NOT_READ_STATE_FROM_MOVIE));
- return false;
- }
- core_serialize_size( &info);
- if (info.size == state_size)
- {
- serial_info.data_const = handle->state;
- serial_info.size = state_size;
- core_unserialize(&serial_info);
- }
- else
- RARCH_WARN("%s\n",
- msg_hash_to_str(MSG_MOVIE_FORMAT_DIFFERENT_SERIALIZER_VERSION));
- }
- handle->min_file_pos = sizeof(header) + state_size;
- return true;
- }
- static bool bsv_movie_init_record(
- bsv_movie_t *handle, const char *path)
- {
- retro_ctx_size_info_t info;
- uint32_t state_size = 0;
- uint32_t content_crc = 0;
- uint32_t header[4] = {0};
- intfstream_t *file = intfstream_open_file(path,
- RETRO_VFS_FILE_ACCESS_WRITE,
- RETRO_VFS_FILE_ACCESS_HINT_NONE);
- if (!file)
- {
- RARCH_ERR("Could not open BSV file for recording, path : \"%s\".\n", path);
- return false;
- }
- handle->file = file;
- content_crc = content_get_crc();
- /* This value is supposed to show up as
- * BSV1 in a HEX editor, big-endian. */
- header[MAGIC_INDEX] = swap_if_little32(BSV_MAGIC);
- header[CRC_INDEX] = swap_if_big32(content_crc);
- core_serialize_size(&info);
- state_size = (unsigned)info.size;
- header[STATE_SIZE_INDEX] = swap_if_big32(state_size);
- intfstream_write(handle->file, header, 4 * sizeof(uint32_t));
- handle->min_file_pos = sizeof(header) + state_size;
- handle->state_size = state_size;
- if (state_size)
- {
- retro_ctx_serialize_info_t serial_info;
- uint8_t *st = (uint8_t*)malloc(state_size);
- if (!st)
- return false;
- handle->state = st;
- serial_info.data = handle->state;
- serial_info.size = state_size;
- core_serialize(&serial_info);
- intfstream_write(handle->file,
- handle->state, state_size);
- }
- return true;
- }
- static void bsv_movie_free(bsv_movie_t *handle)
- {
- if (!handle)
- return;
- intfstream_close(handle->file);
- free(handle->file);
- free(handle->state);
- free(handle->frame_pos);
- free(handle);
- }
- static bsv_movie_t *bsv_movie_init_internal(const char *path,
- enum rarch_movie_type type)
- {
- size_t *frame_pos = NULL;
- bsv_movie_t *handle = (bsv_movie_t*)calloc(1, sizeof(*handle));
- if (!handle)
- return NULL;
- if (type == RARCH_MOVIE_PLAYBACK)
- {
- if (!bsv_movie_init_playback(handle, path))
- goto error;
- }
- else if (!bsv_movie_init_record(handle, path))
- goto error;
- /* Just pick something really large
- * ~1 million frames rewind should do the trick. */
- if (!(frame_pos = (size_t*)calloc((1 << 20), sizeof(size_t))))
- goto error;
- handle->frame_pos = frame_pos;
- handle->frame_pos[0] = handle->min_file_pos;
- handle->frame_mask = (1 << 20) - 1;
- return handle;
- error:
- bsv_movie_free(handle);
- return NULL;
- }
- void bsv_movie_frame_rewind(void)
- {
- struct rarch_state *p_rarch = &rarch_st;
- bsv_movie_t *handle = p_rarch->bsv_movie_state_handle;
- if (!handle)
- return;
- handle->did_rewind = true;
- if ( (handle->frame_ptr <= 1)
- && (handle->frame_pos[0] == handle->min_file_pos))
- {
- /* If we're at the beginning... */
- handle->frame_ptr = 0;
- intfstream_seek(handle->file, (int)handle->min_file_pos, SEEK_SET);
- }
- else
- {
- /* First time rewind is performed, the old frame is simply replayed.
- * However, playing back that frame caused us to read data, and push
- * data to the ring buffer.
- *
- * Sucessively rewinding frames, we need to rewind past the read data,
- * plus another. */
- handle->frame_ptr = (handle->frame_ptr -
- (handle->first_rewind ? 1 : 2)) & handle->frame_mask;
- intfstream_seek(handle->file,
- (int)handle->frame_pos[handle->frame_ptr], SEEK_SET);
- }
- if (intfstream_tell(handle->file) <= (long)handle->min_file_pos)
- {
- /* We rewound past the beginning. */
- if (!handle->playback)
- {
- retro_ctx_serialize_info_t serial_info;
- /* If recording, we simply reset
- * the starting point. Nice and easy. */
- intfstream_seek(handle->file, 4 * sizeof(uint32_t), SEEK_SET);
- serial_info.data = handle->state;
- serial_info.size = handle->state_size;
- core_serialize(&serial_info);
- intfstream_write(handle->file, handle->state, handle->state_size);
- }
- else
- intfstream_seek(handle->file, (int)handle->min_file_pos, SEEK_SET);
- }
- }
- static bool bsv_movie_init_handle(
- struct rarch_state *p_rarch,
- const char *path,
- enum rarch_movie_type type)
- {
- bsv_movie_t *state = bsv_movie_init_internal(path, type);
- if (!state)
- return false;
- p_rarch->bsv_movie_state_handle = state;
- return true;
- }
- static bool bsv_movie_init(struct rarch_state *p_rarch)
- {
- if (p_rarch->bsv_movie_state.movie_start_playback)
- {
- if (!bsv_movie_init_handle(p_rarch,
- p_rarch->bsv_movie_state.movie_start_path,
- RARCH_MOVIE_PLAYBACK))
- {
- RARCH_ERR("%s: \"%s\".\n",
- msg_hash_to_str(MSG_FAILED_TO_LOAD_MOVIE_FILE),
- p_rarch->bsv_movie_state.movie_start_path);
- return false;
- }
- p_rarch->bsv_movie_state.movie_playback = true;
- runloop_msg_queue_push(msg_hash_to_str(MSG_STARTING_MOVIE_PLAYBACK),
- 2, 180, false,
- NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO);
- RARCH_LOG("%s.\n", msg_hash_to_str(MSG_STARTING_MOVIE_PLAYBACK));
- return true;
- }
- else if (p_rarch->bsv_movie_state.movie_start_recording)
- {
- char msg[8192];
- if (!bsv_movie_init_handle(
- p_rarch,
- p_rarch->bsv_movie_state.movie_start_path,
- RARCH_MOVIE_RECORD))
- {
- runloop_msg_queue_push(
- msg_hash_to_str(MSG_FAILED_TO_START_MOVIE_RECORD),
- 1, 180, true,
- NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO);
- RARCH_ERR("%s.\n",
- msg_hash_to_str(MSG_FAILED_TO_START_MOVIE_RECORD));
- return false;
- }
- snprintf(msg, sizeof(msg),
- "%s \"%s\".",
- msg_hash_to_str(MSG_STARTING_MOVIE_RECORD_TO),
- p_rarch->bsv_movie_state.movie_start_path);
- runloop_msg_queue_push(msg, 1, 180, true, NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO);
- RARCH_LOG("%s \"%s\".\n",
- msg_hash_to_str(MSG_STARTING_MOVIE_RECORD_TO),
- p_rarch->bsv_movie_state.movie_start_path);
- return true;
- }
- return false;
- }
- static void bsv_movie_deinit(struct rarch_state *p_rarch)
- {
- if (p_rarch->bsv_movie_state_handle)
- bsv_movie_free(p_rarch->bsv_movie_state_handle);
- p_rarch->bsv_movie_state_handle = NULL;
- }
- static bool runloop_check_movie_init(struct rarch_state *p_rarch)
- {
- char msg[16384], path[8192];
- settings_t *settings = p_rarch->configuration_settings;
- int state_slot = settings->ints.state_slot;
- msg[0] = path[0] = '\0';
- configuration_set_uint(settings, settings->uints.rewind_granularity, 1);
- if (state_slot > 0)
- snprintf(path, sizeof(path), "%s%d.bsv",
- p_rarch->bsv_movie_state.movie_path,
- state_slot);
- else
- {
- strlcpy(path, p_rarch->bsv_movie_state.movie_path, sizeof(path));
- strlcat(path, ".bsv", sizeof(path));
- }
- snprintf(msg, sizeof(msg), "%s \"%s\".",
- msg_hash_to_str(MSG_STARTING_MOVIE_RECORD_TO),
- path);
- bsv_movie_init_handle(
- p_rarch,
- path, RARCH_MOVIE_RECORD);
- if (!p_rarch->bsv_movie_state_handle)
- {
- runloop_msg_queue_push(
- msg_hash_to_str(MSG_FAILED_TO_START_MOVIE_RECORD),
- 2, 180, true,
- NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO);
- RARCH_ERR("%s\n",
- msg_hash_to_str(MSG_FAILED_TO_START_MOVIE_RECORD));
- return false;
- }
- runloop_msg_queue_push(msg, 2, 180, true, NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO);
- RARCH_LOG("%s \"%s\".\n",
- msg_hash_to_str(MSG_STARTING_MOVIE_RECORD_TO),
- path);
- return true;
- }
- static bool bsv_movie_check(struct rarch_state *p_rarch)
- {
- if (!p_rarch->bsv_movie_state_handle)
- return runloop_check_movie_init(p_rarch);
- if (p_rarch->bsv_movie_state.movie_playback)
- {
- /* Checks if movie is being played back. */
- if (!p_rarch->bsv_movie_state.movie_end)
- return false;
- runloop_msg_queue_push(
- msg_hash_to_str(MSG_MOVIE_PLAYBACK_ENDED), 2, 180, false,
- NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO);
- RARCH_LOG("%s\n", msg_hash_to_str(MSG_MOVIE_PLAYBACK_ENDED));
- bsv_movie_deinit(p_rarch);
- p_rarch->bsv_movie_state.movie_end = false;
- p_rarch->bsv_movie_state.movie_playback = false;
- return true;
- }
- /* Checks if movie is being recorded. */
- if (!p_rarch->bsv_movie_state_handle)
- return false;
- runloop_msg_queue_push(
- msg_hash_to_str(MSG_MOVIE_RECORD_STOPPED), 2, 180, true,
- NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO);
- RARCH_LOG("%s\n", msg_hash_to_str(MSG_MOVIE_RECORD_STOPPED));
- bsv_movie_deinit(p_rarch);
- return true;
- }
- #endif
- /* INPUT OVERLAY */
- #ifdef HAVE_OVERLAY
- static bool video_driver_overlay_interface(
- const video_overlay_interface_t **iface);
- /**
- * input_overlay_add_inputs:
- * @ol : pointer to overlay
- * @port : the user to show the inputs of
- *
- * Adds inputs from current_input to the overlay, so it's displayed
- * returns true if an input that is pressed will change the overlay
- */
- static bool input_overlay_add_inputs_inner(overlay_desc_t *desc,
- unsigned port, unsigned analog_dpad_mode)
- {
- switch(desc->type)
- {
- case OVERLAY_TYPE_BUTTONS:
- {
- unsigned i;
- bool all_buttons_pressed = false;
- /*Check each bank of the mask*/
- for (i = 0; i < ARRAY_SIZE(desc->button_mask.data); ++i)
- {
- /*Get bank*/
- uint32_t bank_mask = BITS_GET_ELEM(desc->button_mask,i);
- unsigned id = i * 32;
- /*Worth pursuing? Have we got any bits left in here?*/
- while (bank_mask)
- {
- /*If this bit is set then we need to query the pad
- *The button must be pressed.*/
- if (bank_mask & 1)
- {
- /* Light up the button if pressed */
- if (!input_state(port, RETRO_DEVICE_JOYPAD, 0, id))
- {
- /* We need ALL of the inputs to be active,
- * abort. */
- desc->updated = false;
- return false;
- }
- all_buttons_pressed = true;
- desc->updated = true;
- }
- bank_mask >>= 1;
- ++id;
- }
- }
- return all_buttons_pressed;
- }
- case OVERLAY_TYPE_ANALOG_LEFT:
- case OVERLAY_TYPE_ANALOG_RIGHT:
- {
- unsigned int index = (desc->type == OVERLAY_TYPE_ANALOG_RIGHT) ?
- RETRO_DEVICE_INDEX_ANALOG_RIGHT : RETRO_DEVICE_INDEX_ANALOG_LEFT;
- float analog_x = input_state(port, RETRO_DEVICE_ANALOG,
- index, RETRO_DEVICE_ID_ANALOG_X);
- float analog_y = input_state(port, RETRO_DEVICE_ANALOG,
- index, RETRO_DEVICE_ID_ANALOG_Y);
- float dx = (analog_x/0x8000)*(desc->range_x/2);
- float dy = (analog_y/0x8000)*(desc->range_y/2);
- desc->delta_x = dx;
- desc->delta_y = dy;
- /*Maybe use some option here instead of 0, only display
- changes greater than some magnitude.
- */
- if ((dx * dx) > 0 || (dy*dy) > 0)
- return true;
- }
- break;
- case OVERLAY_TYPE_KEYBOARD:
- if (input_state(port, RETRO_DEVICE_KEYBOARD, 0, desc->retro_key_idx))
- {
- desc->updated = true;
- return true;
- }
- break;
- default:
- break;
- }
- return false;
- }
- static bool input_overlay_add_inputs(input_overlay_t *ol,
- unsigned port, unsigned analog_dpad_mode)
- {
- unsigned i;
- bool button_pressed = false;
- input_overlay_state_t *ol_state = &ol->overlay_state;
- if (!ol_state)
- return false;
- for (i = 0; i < ol->active->size; i++)
- {
- overlay_desc_t *desc = &(ol->active->descs[i]);
- button_pressed |= input_overlay_add_inputs_inner(desc,
- port, analog_dpad_mode);
- }
- return button_pressed;
- }
- static void input_overlay_parse_layout(
- const struct overlay *ol,
- const overlay_layout_desc_t *layout_desc,
- float display_aspect_ratio,
- overlay_layout_t *overlay_layout)
- {
- /* Set default values */
- overlay_layout->x_scale = 1.0f;
- overlay_layout->y_scale = 1.0f;
- overlay_layout->x_separation = 0.0f;
- overlay_layout->y_separation = 0.0f;
- overlay_layout->x_offset = 0.0f;
- overlay_layout->y_offset = 0.0f;
- /* Perform auto-scaling, if required */
- if (layout_desc->auto_scale)
- {
- /* Sanity check - if scaling is blocked,
- * or aspect ratios are invalid, then we
- * can do nothing */
- if (ol->block_scale ||
- (ol->aspect_ratio <= 0.0f) ||
- (display_aspect_ratio <= 0.0f))
- return;
- /* If display is wider than overlay,
- * reduce width */
- if (display_aspect_ratio >
- ol->aspect_ratio)
- {
- overlay_layout->x_scale = ol->aspect_ratio /
- display_aspect_ratio;
- if (overlay_layout->x_scale <= 0.0f)
- {
- overlay_layout->x_scale = 1.0f;
- return;
- }
- /* If X separation is permitted, move elements
- * horizontally towards the edges of the screen */
- if (!ol->block_x_separation)
- overlay_layout->x_separation = ((1.0f / overlay_layout->x_scale) - 1.0f) * 0.5f;
- }
- /* If display is taller than overlay,
- * reduce height */
- else
- {
- overlay_layout->y_scale = display_aspect_ratio /
- ol->aspect_ratio;
- if (overlay_layout->y_scale <= 0.0f)
- {
- overlay_layout->y_scale = 1.0f;
- return;
- }
- /* If Y separation is permitted and display has
- * a *landscape* orientation, move elements
- * vertically towards the edges of the screen
- * > Portrait overlays typically have all elements
- * below the centre line, so Y separation
- * provides no real benefit */
- if ((display_aspect_ratio > 1.0f) &&
- !ol->block_y_separation)
- overlay_layout->y_separation = ((1.0f / overlay_layout->y_scale) - 1.0f) * 0.5f;
- }
- return;
- }
- /* Regular 'manual' scaling/position adjustment
- * > Landscape display orientations */
- if (display_aspect_ratio > 1.0f)
- {
- float scale = layout_desc->scale_landscape;
- float aspect_adjust = layout_desc->aspect_adjust_landscape;
- /* Note: Y offsets have their sign inverted,
- * since from a usability perspective positive
- * values should move the overlay upwards */
- overlay_layout->x_offset = layout_desc->x_offset_landscape;
- overlay_layout->y_offset = layout_desc->y_offset_landscape * -1.0f;
- if (!ol->block_x_separation)
- overlay_layout->x_separation = layout_desc->x_separation_landscape;
- if (!ol->block_y_separation)
- overlay_layout->y_separation = layout_desc->y_separation_landscape;
- if (!ol->block_scale)
- {
- /* In landscape orientations, aspect correction
- * adjusts the overlay width */
- overlay_layout->x_scale = (aspect_adjust >= 0.0f) ?
- (scale * (aspect_adjust + 1.0f)) :
- (scale / ((aspect_adjust * -1.0f) + 1.0f));
- overlay_layout->y_scale = scale;
- }
- }
- /* > Portrait display orientations */
- else
- {
- float scale = layout_desc->scale_portrait;
- float aspect_adjust = layout_desc->aspect_adjust_portrait;
- overlay_layout->x_offset = layout_desc->x_offset_portrait;
- overlay_layout->y_offset = layout_desc->y_offset_portrait * -1.0f;
- if (!ol->block_x_separation)
- overlay_layout->x_separation = layout_desc->x_separation_portrait;
- if (!ol->block_y_separation)
- overlay_layout->y_separation = layout_desc->y_separation_portrait;
- if (!ol->block_scale)
- {
- /* In portrait orientations, aspect correction
- * adjusts the overlay height */
- overlay_layout->x_scale = scale;
- overlay_layout->y_scale = (aspect_adjust >= 0.0f) ?
- (scale * (aspect_adjust + 1.0f)) :
- (scale / ((aspect_adjust * -1.0f) + 1.0f));
- }
- }
- }
- /**
- * input_overlay_scale:
- * @ol : Overlay handle.
- * @layout : Scale + offset factors.
- *
- * Scales the overlay and all its associated descriptors
- * and applies any aspect ratio/offset factors.
- **/
- static void input_overlay_scale(struct overlay *ol,
- const overlay_layout_t *layout)
- {
- size_t i;
- ol->mod_w = ol->w * layout->x_scale;
- ol->mod_h = ol->h * layout->y_scale;
- ol->mod_x = (ol->center_x + (ol->x - ol->center_x) *
- layout->x_scale) + layout->x_offset;
- ol->mod_y = (ol->center_y + (ol->y - ol->center_y) *
- layout->y_scale) + layout->y_offset;
- for (i = 0; i < ol->size; i++)
- {
- struct overlay_desc *desc = &ol->descs[i];
- float x_shift_offset = 0.0f;
- float y_shift_offset = 0.0f;
- float scale_w;
- float scale_h;
- float adj_center_x;
- float adj_center_y;
- /* Apply 'x separation' factor */
- if (desc->x < (0.5f - 0.0001f))
- x_shift_offset = layout->x_separation * -1.0f;
- else if (desc->x > (0.5f + 0.0001f))
- x_shift_offset = layout->x_separation;
- desc->x_shift = desc->x + x_shift_offset;
- /* Apply 'y separation' factor */
- if (desc->y < (0.5f - 0.0001f))
- y_shift_offset = layout->y_separation * -1.0f;
- else if (desc->y > (0.5f + 0.0001f))
- y_shift_offset = layout->y_separation;
- desc->y_shift = desc->y + y_shift_offset;
- scale_w = ol->mod_w * desc->range_x;
- scale_h = ol->mod_h * desc->range_y;
- adj_center_x = ol->mod_x + desc->x_shift * ol->mod_w;
- adj_center_y = ol->mod_y + desc->y_shift * ol->mod_h;
- desc->mod_w = 2.0f * scale_w;
- desc->mod_h = 2.0f * scale_h;
- desc->mod_x = adj_center_x - scale_w;
- desc->mod_y = adj_center_y - scale_h;
- }
- }
- static void input_overlay_set_vertex_geom(input_overlay_t *ol)
- {
- size_t i;
- if (ol->active->image.pixels)
- ol->iface->vertex_geom(ol->iface_data, 0,
- ol->active->mod_x, ol->active->mod_y,
- ol->active->mod_w, ol->active->mod_h);
- if (ol->iface->vertex_geom)
- for (i = 0; i < ol->active->size; i++)
- {
- struct overlay_desc *desc = &ol->active->descs[i];
- if (!desc->image.pixels)
- continue;
- ol->iface->vertex_geom(ol->iface_data, desc->image_index,
- desc->mod_x, desc->mod_y, desc->mod_w, desc->mod_h);
- }
- }
- /**
- * input_overlay_set_scale_factor:
- * @ol : Overlay handle.
- * @layout_desc : Scale + offset factors.
- *
- * Scales the overlay and applies any aspect ratio/
- * offset factors.
- **/
- static void input_overlay_set_scale_factor(struct rarch_state *p_rarch,
- input_overlay_t *ol, const overlay_layout_desc_t *layout_desc)
- {
- float display_aspect_ratio = 0.0f;
- size_t i;
- if (!ol || !layout_desc)
- return;
- if (p_rarch->video_driver_height > 0)
- display_aspect_ratio = (float)p_rarch->video_driver_width /
- (float)p_rarch->video_driver_height;
- for (i = 0; i < ol->size; i++)
- {
- struct overlay *current_overlay = &ol->overlays[i];
- overlay_layout_t overlay_layout;
- input_overlay_parse_layout(current_overlay,
- layout_desc, display_aspect_ratio, &overlay_layout);
- input_overlay_scale(current_overlay, &overlay_layout);
- }
- input_overlay_set_vertex_geom(ol);
- }
- void input_overlay_free_overlay(struct overlay *overlay)
- {
- size_t i;
- if (!overlay)
- return;
- for (i = 0; i < overlay->size; i++)
- image_texture_free(&overlay->descs[i].image);
- if (overlay->load_images)
- free(overlay->load_images);
- overlay->load_images = NULL;
- if (overlay->descs)
- free(overlay->descs);
- overlay->descs = NULL;
- image_texture_free(&overlay->image);
- }
- static void input_overlay_free_overlays(input_overlay_t *ol)
- {
- size_t i;
- if (!ol || !ol->overlays)
- return;
- for (i = 0; i < ol->size; i++)
- input_overlay_free_overlay(&ol->overlays[i]);
- free(ol->overlays);
- ol->overlays = NULL;
- }
- static enum overlay_visibility input_overlay_get_visibility(
- struct rarch_state *p_rarch,
- int overlay_idx)
- {
- enum overlay_visibility *visibility = p_rarch->overlay_visibility;
- if (!visibility)
- return OVERLAY_VISIBILITY_DEFAULT;
- if ((overlay_idx < 0) || (overlay_idx >= MAX_VISIBILITY))
- return OVERLAY_VISIBILITY_DEFAULT;
- return visibility[overlay_idx];
- }
- static bool input_overlay_is_hidden(
- struct rarch_state *p_rarch,
- int overlay_idx)
- {
- return (input_overlay_get_visibility(p_rarch, overlay_idx)
- == OVERLAY_VISIBILITY_HIDDEN);
- }
- /**
- * input_overlay_set_alpha_mod:
- * @ol : Overlay handle.
- * @mod : New modulating factor to apply.
- *
- * Sets a modulating factor for alpha channel. Default is 1.0.
- * The alpha factor is applied for all overlays.
- **/
- static void input_overlay_set_alpha_mod(
- struct rarch_state *p_rarch,
- input_overlay_t *ol, float mod)
- {
- unsigned i;
- if (!ol)
- return;
- for (i = 0; i < ol->active->load_images_size; i++)
- {
- if (input_overlay_is_hidden(p_rarch, i))
- ol->iface->set_alpha(ol->iface_data, i, 0.0);
- else
- ol->iface->set_alpha(ol->iface_data, i, mod);
- }
- }
- static void input_overlay_load_active(
- struct rarch_state *p_rarch,
- input_overlay_t *ol, float opacity)
- {
- if (ol->iface->load)
- ol->iface->load(ol->iface_data, ol->active->load_images,
- ol->active->load_images_size);
- input_overlay_set_alpha_mod(p_rarch, ol, opacity);
- input_overlay_set_vertex_geom(ol);
- if (ol->iface->full_screen)
- ol->iface->full_screen(ol->iface_data, ol->active->full_screen);
- }
- /* Attempts to automatically rotate the specified overlay.
- * Depends upon proper naming conventions in overlay
- * config file. */
- static void input_overlay_auto_rotate_(
- struct rarch_state *p_rarch, input_overlay_t *ol)
- {
- size_t i;
- enum overlay_orientation screen_orientation = OVERLAY_ORIENTATION_NONE;
- enum overlay_orientation active_overlay_orientation = OVERLAY_ORIENTATION_NONE;
- settings_t *settings = p_rarch->configuration_settings;
- bool input_overlay_enable = settings->bools.input_overlay_enable;
- bool next_overlay_found = false;
- bool tmp = false;
- unsigned next_overlay_index = 0;
- /* Sanity check */
- if (!ol)
- return;
- if (!ol->alive || !input_overlay_enable)
- return;
- /* Get current screen orientation */
- if (p_rarch->video_driver_width > p_rarch->video_driver_height)
- screen_orientation = OVERLAY_ORIENTATION_LANDSCAPE;
- else
- screen_orientation = OVERLAY_ORIENTATION_PORTRAIT;
- /* Get orientation of active overlay */
- if (!string_is_empty(ol->active->name))
- {
- if (strstr(ol->active->name, "landscape"))
- active_overlay_orientation = OVERLAY_ORIENTATION_LANDSCAPE;
- else if (strstr(ol->active->name, "portrait"))
- active_overlay_orientation = OVERLAY_ORIENTATION_PORTRAIT;
- }
- /* Sanity check */
- if (active_overlay_orientation == OVERLAY_ORIENTATION_NONE)
- return;
- /* If screen and overlay have the same orientation,
- * no action is required */
- if (screen_orientation == active_overlay_orientation)
- return;
- /* Attempt to find index of overlay corresponding
- * to opposite orientation */
- for (i = 0; i < p_rarch->overlay_ptr->active->size; i++)
- {
- overlay_desc_t *desc = &p_rarch->overlay_ptr->active->descs[i];
- if (!desc)
- continue;
- if (!string_is_empty(desc->next_index_name))
- {
- if (active_overlay_orientation == OVERLAY_ORIENTATION_LANDSCAPE)
- next_overlay_found = (strstr(desc->next_index_name, "portrait") != 0);
- else
- next_overlay_found = (strstr(desc->next_index_name, "landscape") != 0);
- if (next_overlay_found)
- {
- next_overlay_index = desc->next_index;
- break;
- }
- }
- }
- /* Sanity check */
- if (!next_overlay_found)
- return;
- /* We have a valid target overlay
- * > Trigger 'overly next' command event
- * Note: tmp == false. This prevents CMD_EVENT_OVERLAY_NEXT
- * from calling input_overlay_auto_rotate_() again */
- ol->next_index = next_overlay_index;
- command_event(CMD_EVENT_OVERLAY_NEXT, &tmp);
- }
- /**
- * inside_hitbox:
- * @desc : Overlay descriptor handle.
- * @x : X coordinate value.
- * @y : Y coordinate value.
- *
- * Check whether the given @x and @y coordinates of the overlay
- * descriptor @desc is inside the overlay descriptor's hitbox.
- *
- * Returns: true (1) if X, Y coordinates are inside a hitbox,
- * otherwise false (0).
- **/
- static bool inside_hitbox(const struct overlay_desc *desc, float x, float y)
- {
- if (!desc)
- return false;
- switch (desc->hitbox)
- {
- case OVERLAY_HITBOX_RADIAL:
- {
- /* Ellipsis. */
- float x_dist = (x - desc->x_shift) / desc->range_x_mod;
- float y_dist = (y - desc->y_shift) / desc->range_y_mod;
- float sq_dist = x_dist * x_dist + y_dist * y_dist;
- return (sq_dist <= 1.0f);
- }
- case OVERLAY_HITBOX_RECT:
- return
- (fabs(x - desc->x_shift) <= desc->range_x_mod) &&
- (fabs(y - desc->y_shift) <= desc->range_y_mod);
- }
- return false;
- }
- /**
- * input_overlay_poll:
- * @out : Polled output data.
- * @norm_x : Normalized X coordinate.
- * @norm_y : Normalized Y coordinate.
- *
- * Polls input overlay.
- *
- * @norm_x and @norm_y are the result of
- * input_translate_coord_viewport().
- **/
- static void input_overlay_poll(
- input_overlay_t *ol,
- input_overlay_state_t *out,
- int16_t norm_x, int16_t norm_y)
- {
- size_t i;
- /* norm_x and norm_y is in [-0x7fff, 0x7fff] range,
- * like RETRO_DEVICE_POINTER. */
- float x = (float)(norm_x + 0x7fff) / 0xffff;
- float y = (float)(norm_y + 0x7fff) / 0xffff;
- x -= ol->active->mod_x;
- y -= ol->active->mod_y;
- x /= ol->active->mod_w;
- y /= ol->active->mod_h;
- for (i = 0; i < ol->active->size; i++)
- {
- float x_dist, y_dist;
- unsigned int base = 0;
- struct overlay_desc *desc = &ol->active->descs[i];
- if (!inside_hitbox(desc, x, y))
- continue;
- desc->updated = true;
- x_dist = x - desc->x_shift;
- y_dist = y - desc->y_shift;
- switch (desc->type)
- {
- case OVERLAY_TYPE_BUTTONS:
- {
- bits_or_bits(out->buttons.data,
- desc->button_mask.data,
- ARRAY_SIZE(desc->button_mask.data));
- if (BIT256_GET(desc->button_mask, RARCH_OVERLAY_NEXT))
- ol->next_index = desc->next_index;
- }
- break;
- case OVERLAY_TYPE_KEYBOARD:
- if (desc->retro_key_idx < RETROK_LAST)
- OVERLAY_SET_KEY(out, desc->retro_key_idx);
- break;
- case OVERLAY_TYPE_ANALOG_RIGHT:
- base = 2;
- /* fall-through */
- default:
- {
- float x_val = x_dist / desc->range_x;
- float y_val = y_dist / desc->range_y;
- float x_val_sat = x_val / desc->analog_saturate_pct;
- float y_val_sat = y_val / desc->analog_saturate_pct;
- out->analog[base + 0] = clamp_float(x_val_sat, -1.0f, 1.0f)
- * 32767.0f;
- out->analog[base + 1] = clamp_float(y_val_sat, -1.0f, 1.0f)
- * 32767.0f;
- }
- break;
- }
- if (desc->movable)
- {
- desc->delta_x = clamp_float(x_dist, -desc->range_x, desc->range_x)
- * ol->active->mod_w;
- desc->delta_y = clamp_float(y_dist, -desc->range_y, desc->range_y)
- * ol->active->mod_h;
- }
- }
- if (!bits_any_set(out->buttons.data, ARRAY_SIZE(out->buttons.data)))
- ol->blocked = false;
- else if (ol->blocked)
- memset(out, 0, sizeof(*out));
- }
- /**
- * input_overlay_update_desc_geom:
- * @ol : overlay handle.
- * @desc : overlay descriptors handle.
- *
- * Update input overlay descriptors' vertex geometry.
- **/
- static void input_overlay_update_desc_geom(input_overlay_t *ol,
- struct overlay_desc *desc)
- {
- if (!desc->image.pixels || !desc->movable)
- return;
- if (ol->iface->vertex_geom)
- ol->iface->vertex_geom(ol->iface_data, desc->image_index,
- desc->mod_x + desc->delta_x, desc->mod_y + desc->delta_y,
- desc->mod_w, desc->mod_h);
- desc->delta_x = 0.0f;
- desc->delta_y = 0.0f;
- }
- /**
- * input_overlay_post_poll:
- *
- * Called after all the input_overlay_poll() calls to
- * update the range modifiers for pressed/unpressed regions
- * and alpha mods.
- **/
- static void input_overlay_post_poll(
- struct rarch_state *p_rarch,
- input_overlay_t *ol, float opacity)
- {
- size_t i;
- input_overlay_set_alpha_mod(p_rarch, ol, opacity);
- for (i = 0; i < ol->active->size; i++)
- {
- struct overlay_desc *desc = &ol->active->descs[i];
- desc->range_x_mod = desc->range_x;
- desc->range_y_mod = desc->range_y;
- if (desc->updated)
- {
- /* If pressed this frame, change the hitbox. */
- desc->range_x_mod *= desc->range_mod;
- desc->range_y_mod *= desc->range_mod;
- if (desc->image.pixels)
- {
- if (ol->iface->set_alpha)
- ol->iface->set_alpha(ol->iface_data, desc->image_index,
- desc->alpha_mod * opacity);
- }
- }
- input_overlay_update_desc_geom(ol, desc);
- desc->updated = false;
- }
- }
- /**
- * input_overlay_poll_clear:
- * @ol : overlay handle
- *
- * Call when there is nothing to poll. Allows overlay to
- * clear certain state.
- **/
- static void input_overlay_poll_clear(
- struct rarch_state *p_rarch,
- input_overlay_t *ol, float opacity)
- {
- size_t i;
- ol->blocked = false;
- input_overlay_set_alpha_mod(p_rarch, ol, opacity);
- for (i = 0; i < ol->active->size; i++)
- {
- struct overlay_desc *desc = &ol->active->descs[i];
- desc->range_x_mod = desc->range_x;
- desc->range_y_mod = desc->range_y;
- desc->updated = false;
- desc->delta_x = 0.0f;
- desc->delta_y = 0.0f;
- input_overlay_update_desc_geom(ol, desc);
- }
- }
- /**
- * input_overlay_free:
- * @ol : Overlay handle.
- *
- * Frees overlay handle.
- **/
- static void input_overlay_free(input_overlay_t *ol)
- {
- if (!ol)
- return;
- input_overlay_free_overlays(ol);
- if (ol->iface->enable)
- ol->iface->enable(ol->iface_data, false);
- free(ol);
- }
- /* task_data = overlay_task_data_t* */
- static void input_overlay_loaded(retro_task_t *task,
- void *task_data, void *user_data, const char *err)
- {
- size_t i;
- struct rarch_state *p_rarch = &rarch_st;
- overlay_task_data_t *data = (overlay_task_data_t*)task_data;
- input_overlay_t *ol = NULL;
- const video_overlay_interface_t *iface = NULL;
- settings_t *settings = p_rarch->configuration_settings;
- bool input_overlay_show_mouse_cursor = settings->bools.input_overlay_show_mouse_cursor;
- bool inp_overlay_auto_rotate = settings->bools.input_overlay_auto_rotate;
- if (err)
- return;
- if (data->overlay_enable)
- {
- #ifdef HAVE_MENU
- /* We can't display when the menu is up */
- if (data->hide_in_menu && p_rarch->menu_driver_alive)
- goto abort_load;
- #endif
- /* If 'hide_when_gamepad_connected' is enabled,
- * we can't display when a gamepad is connected */
- if (data->hide_when_gamepad_connected &&
- (input_config_get_device_name(0) != NULL))
- goto abort_load;
- }
- if ( !data->overlay_enable ||
- !video_driver_overlay_interface(&iface) ||
- !iface)
- {
- RARCH_ERR("Overlay interface is not present in video driver,"
- " or not enabled.\n");
- goto abort_load;
- }
- ol = (input_overlay_t*)calloc(1, sizeof(*ol));
- ol->overlays = data->overlays;
- ol->size = data->size;
- ol->active = data->active;
- ol->iface = iface;
- ol->iface_data = VIDEO_DRIVER_GET_PTR_INTERNAL(p_rarch, true);
- input_overlay_load_active(p_rarch, ol, data->overlay_opacity);
- /* Enable or disable the overlay. */
- ol->enable = data->overlay_enable;
- if (ol->iface->enable)
- ol->iface->enable(ol->iface_data, data->overlay_enable);
- input_overlay_set_scale_factor(p_rarch, ol, &data->layout_desc);
- ol->next_index = (unsigned)((ol->index + 1) % ol->size);
- ol->state = OVERLAY_STATUS_NONE;
- ol->alive = true;
- /* Due to the asynchronous nature of overlay loading
- * it is possible for overlay_ptr to be non-NULL here
- * > Ensure it is free()'d before assigning new pointer */
- if (p_rarch->overlay_ptr)
- {
- input_overlay_free_overlays(p_rarch->overlay_ptr);
- free(p_rarch->overlay_ptr);
- }
- p_rarch->overlay_ptr = ol;
- free(data);
- if (!input_overlay_show_mouse_cursor)
- video_driver_hide_mouse();
- /* Attempt to automatically rotate overlay, if required */
- if (inp_overlay_auto_rotate)
- input_overlay_auto_rotate_(p_rarch,
- p_rarch->overlay_ptr);
- return;
- abort_load:
- for (i = 0; i < data->size; i++)
- input_overlay_free_overlay(&data->overlays[i]);
- free(data->overlays);
- free(data);
- }
- void input_overlay_set_visibility(int overlay_idx,
- enum overlay_visibility vis)
- {
- struct rarch_state *p_rarch = &rarch_st;
- input_overlay_t *ol = p_rarch->overlay_ptr;
- if (!p_rarch->overlay_visibility)
- {
- unsigned i;
- p_rarch->overlay_visibility = (enum overlay_visibility *)calloc(
- MAX_VISIBILITY, sizeof(enum overlay_visibility));
- for (i = 0; i < MAX_VISIBILITY; i++)
- p_rarch->overlay_visibility[i] = OVERLAY_VISIBILITY_DEFAULT;
- }
- p_rarch->overlay_visibility[overlay_idx] = vis;
- if (!ol)
- return;
- if (vis == OVERLAY_VISIBILITY_HIDDEN)
- ol->iface->set_alpha(ol->iface_data, overlay_idx, 0.0);
- }
- /*
- * input_poll_overlay:
- *
- * Poll pressed buttons/keys on currently active overlay.
- **/
- static void input_poll_overlay(
- struct rarch_state *p_rarch,
- input_overlay_t *ol, float opacity,
- unsigned analog_dpad_mode,
- float axis_threshold)
- {
- input_overlay_state_t old_key_state;
- unsigned i, j;
- uint16_t key_mod = 0;
- bool polled = false;
- bool button_pressed = false;
- void *input_data = p_rarch->current_input_data;
- input_overlay_state_t *ol_state = &ol->overlay_state;
- input_driver_t *current_input = p_rarch->current_input;
- settings_t *settings = p_rarch->configuration_settings;
- bool input_overlay_show_physical_inputs = settings->bools.input_overlay_show_physical_inputs;
- unsigned input_overlay_show_physical_inputs_port = settings->uints.input_overlay_show_physical_inputs_port;
- if (!ol_state)
- return;
- memcpy(old_key_state.keys, ol_state->keys,
- sizeof(ol_state->keys));
- memset(ol_state, 0, sizeof(*ol_state));
- if (current_input->input_state)
- {
- rarch_joypad_info_t joypad_info;
- unsigned device = ol->active->full_screen
- ? RARCH_DEVICE_POINTER_SCREEN
- : RETRO_DEVICE_POINTER;
- #ifdef HAVE_MFI
- const input_device_driver_t
- *sec_joypad = p_rarch->sec_joypad;
- #else
- const input_device_driver_t
- *sec_joypad = NULL;
- #endif
- joypad_info.joy_idx = 0;
- joypad_info.auto_binds = NULL;
- joypad_info.axis_threshold = 0.0f;
- for (i = 0;
- current_input->input_state(
- input_data,
- p_rarch->joypad,
- sec_joypad,
- &joypad_info,
- NULL,
- p_rarch->keyboard_mapping_blocked,
- 0,
- device,
- i,
- RETRO_DEVICE_ID_POINTER_PRESSED);
- i++)
- {
- input_overlay_state_t polled_data;
- int16_t x = current_input->input_state(
- input_data,
- p_rarch->joypad,
- sec_joypad,
- &joypad_info,
- NULL,
- p_rarch->keyboard_mapping_blocked,
- 0,
- device,
- i,
- RETRO_DEVICE_ID_POINTER_X);
- int16_t y = current_input->input_state(
- input_data,
- p_rarch->joypad,
- sec_joypad,
- &joypad_info,
- NULL,
- p_rarch->keyboard_mapping_blocked,
- 0,
- device,
- i,
- RETRO_DEVICE_ID_POINTER_Y);
- memset(&polled_data, 0, sizeof(struct input_overlay_state));
- if (ol->enable)
- input_overlay_poll(ol, &polled_data, x, y);
- else
- ol->blocked = false;
- bits_or_bits(ol_state->buttons.data,
- polled_data.buttons.data,
- ARRAY_SIZE(polled_data.buttons.data));
- for (j = 0; j < ARRAY_SIZE(ol_state->keys); j++)
- ol_state->keys[j] |= polled_data.keys[j];
- /* Fingers pressed later take priority and matched up
- * with overlay poll priorities. */
- for (j = 0; j < 4; j++)
- if (polled_data.analog[j])
- ol_state->analog[j] = polled_data.analog[j];
- polled = true;
- }
- }
- if ( OVERLAY_GET_KEY(ol_state, RETROK_LSHIFT) ||
- OVERLAY_GET_KEY(ol_state, RETROK_RSHIFT))
- key_mod |= RETROKMOD_SHIFT;
- if (OVERLAY_GET_KEY(ol_state, RETROK_LCTRL) ||
- OVERLAY_GET_KEY(ol_state, RETROK_RCTRL))
- key_mod |= RETROKMOD_CTRL;
- if ( OVERLAY_GET_KEY(ol_state, RETROK_LALT) ||
- OVERLAY_GET_KEY(ol_state, RETROK_RALT))
- key_mod |= RETROKMOD_ALT;
- if ( OVERLAY_GET_KEY(ol_state, RETROK_LMETA) ||
- OVERLAY_GET_KEY(ol_state, RETROK_RMETA))
- key_mod |= RETROKMOD_META;
- /* CAPSLOCK SCROLLOCK NUMLOCK */
- for (i = 0; i < ARRAY_SIZE(ol_state->keys); i++)
- {
- if (ol_state->keys[i] != old_key_state.keys[i])
- {
- uint32_t orig_bits = old_key_state.keys[i];
- uint32_t new_bits = ol_state->keys[i];
- for (j = 0; j < 32; j++)
- if ((orig_bits & (1 << j)) != (new_bits & (1 << j)))
- input_keyboard_event(new_bits & (1 << j),
- i * 32 + j, 0, key_mod, RETRO_DEVICE_POINTER);
- }
- }
- /* Map "analog" buttons to analog axes like regular input drivers do. */
- for (j = 0; j < 4; j++)
- {
- unsigned bind_plus = RARCH_ANALOG_LEFT_X_PLUS + 2 * j;
- unsigned bind_minus = bind_plus + 1;
- if (ol_state->analog[j])
- continue;
- if ((BIT256_GET(ol->overlay_state.buttons, bind_plus)))
- ol_state->analog[j] += 0x7fff;
- if ((BIT256_GET(ol->overlay_state.buttons, bind_minus)))
- ol_state->analog[j] -= 0x7fff;
- }
- /* Check for analog_dpad_mode.
- * Map analogs to d-pad buttons when configured. */
- switch (analog_dpad_mode)
- {
- case ANALOG_DPAD_LSTICK:
- case ANALOG_DPAD_RSTICK:
- {
- float analog_x, analog_y;
- unsigned analog_base = 2;
- if (analog_dpad_mode == ANALOG_DPAD_LSTICK)
- analog_base = 0;
- analog_x = (float)ol_state->analog[analog_base + 0] / 0x7fff;
- analog_y = (float)ol_state->analog[analog_base + 1] / 0x7fff;
- if (analog_x <= -axis_threshold)
- BIT256_SET(ol_state->buttons, RETRO_DEVICE_ID_JOYPAD_LEFT);
- if (analog_x >= axis_threshold)
- BIT256_SET(ol_state->buttons, RETRO_DEVICE_ID_JOYPAD_RIGHT);
- if (analog_y <= -axis_threshold)
- BIT256_SET(ol_state->buttons, RETRO_DEVICE_ID_JOYPAD_UP);
- if (analog_y >= axis_threshold)
- BIT256_SET(ol_state->buttons, RETRO_DEVICE_ID_JOYPAD_DOWN);
- break;
- }
- default:
- break;
- }
- if (input_overlay_show_physical_inputs)
- button_pressed = input_overlay_add_inputs(ol,
- input_overlay_show_physical_inputs_port,
- analog_dpad_mode);
- if (button_pressed || polled)
- input_overlay_post_poll(p_rarch, ol, opacity);
- else
- input_overlay_poll_clear(p_rarch, ol, opacity);
- }
- static void retroarch_overlay_deinit(struct rarch_state *p_rarch)
- {
- input_overlay_free(p_rarch->overlay_ptr);
- p_rarch->overlay_ptr = NULL;
- }
- static void retroarch_overlay_init(struct rarch_state *p_rarch)
- {
- settings_t *settings = p_rarch->configuration_settings;
- bool input_overlay_enable = settings->bools.input_overlay_enable;
- bool input_overlay_auto_scale = settings->bools.input_overlay_auto_scale;
- const char *path_overlay = settings->paths.path_overlay;
- float overlay_opacity = settings->floats.input_overlay_opacity;
- float overlay_scale_landscape = settings->floats.input_overlay_scale_landscape;
- float overlay_aspect_adjust_landscape = settings->floats.input_overlay_aspect_adjust_landscape;
- float overlay_x_separation_landscape = settings->floats.input_overlay_x_separation_landscape;
- float overlay_y_separation_landscape = settings->floats.input_overlay_y_separation_landscape;
- float overlay_x_offset_landscape = settings->floats.input_overlay_x_offset_landscape;
- float overlay_y_offset_landscape = settings->floats.input_overlay_y_offset_landscape;
- float overlay_scale_portrait = settings->floats.input_overlay_scale_portrait;
- float overlay_aspect_adjust_portrait = settings->floats.input_overlay_aspect_adjust_portrait;
- float overlay_x_separation_portrait = settings->floats.input_overlay_x_separation_portrait;
- float overlay_y_separation_portrait = settings->floats.input_overlay_y_separation_portrait;
- float overlay_x_offset_portrait = settings->floats.input_overlay_x_offset_portrait;
- float overlay_y_offset_portrait = settings->floats.input_overlay_y_offset_portrait;
- bool load_enabled = input_overlay_enable;
- #ifdef HAVE_MENU
- bool overlay_hide_in_menu = settings->bools.input_overlay_hide_in_menu;
- #else
- bool overlay_hide_in_menu = false;
- #endif
- bool overlay_hide_when_gamepad_connected = settings->bools.input_overlay_hide_when_gamepad_connected;
- #if defined(GEKKO)
- /* Avoid a crash at startup or even when toggling overlay in rgui */
- uint64_t memory_free = frontend_driver_get_free_memory();
- if (memory_free < (3 * 1024 * 1024))
- return;
- #endif
- retroarch_overlay_deinit(p_rarch);
- #ifdef HAVE_MENU
- /* Cancel load if 'hide_in_menu' is enabled and
- * menu is currently active */
- if (overlay_hide_in_menu)
- load_enabled = load_enabled && !p_rarch->menu_driver_alive;
- #endif
- /* Cancel load if 'hide_when_gamepad_connected' is
- * enabled and a gamepad is currently connected */
- if (overlay_hide_when_gamepad_connected)
- load_enabled = load_enabled && (input_config_get_device_name(0) == NULL);
- if (load_enabled)
- {
- overlay_layout_desc_t layout_desc;
- layout_desc.scale_landscape = overlay_scale_landscape;
- layout_desc.aspect_adjust_landscape = overlay_aspect_adjust_landscape;
- layout_desc.x_separation_landscape = overlay_x_separation_landscape;
- layout_desc.y_separation_landscape = overlay_y_separation_landscape;
- layout_desc.x_offset_landscape = overlay_x_offset_landscape;
- layout_desc.y_offset_landscape = overlay_y_offset_landscape;
- layout_desc.scale_portrait = overlay_scale_portrait;
- layout_desc.aspect_adjust_portrait = overlay_aspect_adjust_portrait;
- layout_desc.x_separation_portrait = overlay_x_separation_portrait;
- layout_desc.y_separation_portrait = overlay_y_separation_portrait;
- layout_desc.x_offset_portrait = overlay_x_offset_portrait;
- layout_desc.y_offset_portrait = overlay_y_offset_portrait;
- layout_desc.auto_scale = input_overlay_auto_scale;
- task_push_overlay_load_default(input_overlay_loaded,
- path_overlay,
- overlay_hide_in_menu,
- overlay_hide_when_gamepad_connected,
- input_overlay_enable,
- overlay_opacity,
- &layout_desc,
- NULL);
- }
- }
- #endif
- /* INPUT REMOTE */
- #if defined(HAVE_NETWORKING) && defined(HAVE_NETWORKGAMEPAD)
- static bool input_remote_init_network(input_remote_t *handle,
- uint16_t port, unsigned user)
- {
- int fd;
- struct addrinfo *res = NULL;
- port = port + user;
- if (!network_init())
- return false;
- RARCH_LOG("Bringing up remote interface on port %hu.\n",
- (unsigned short)port);
- fd = socket_init((void**)&res, port, NULL, SOCKET_TYPE_DATAGRAM);
- if (fd < 0)
- goto error;
- handle->net_fd[user] = fd;
- if (!socket_nonblock(handle->net_fd[user]))
- goto error;
- if (!socket_bind(handle->net_fd[user], res))
- {
- RARCH_ERR("%s\n", msg_hash_to_str(MSG_FAILED_TO_BIND_SOCKET));
- goto error;
- }
- freeaddrinfo_retro(res);
- return true;
- error:
- if (res)
- freeaddrinfo_retro(res);
- return false;
- }
- static void input_remote_free(input_remote_t *handle, unsigned max_users)
- {
- unsigned user;
- for (user = 0; user < max_users; user ++)
- socket_close(handle->net_fd[user]);
- free(handle);
- }
- static input_remote_t *input_remote_new(
- settings_t *settings,
- uint16_t port, unsigned max_users)
- {
- unsigned user;
- input_remote_t *handle = (input_remote_t*)
- calloc(1, sizeof(*handle));
- if (!handle)
- return NULL;
- for (user = 0; user < max_users; user ++)
- {
- handle->net_fd[user] = -1;
- if (settings->bools.network_remote_enable_user[user])
- if (!input_remote_init_network(handle, port, user))
- {
- input_remote_free(handle, max_users);
- return NULL;
- }
- }
- return handle;
- }
- static void input_remote_parse_packet(
- input_remote_state_t *input_state,
- struct remote_message *msg, unsigned user)
- {
- /* Parse message */
- switch (msg->device)
- {
- case RETRO_DEVICE_JOYPAD:
- input_state->buttons[user] &= ~(1 << msg->id);
- if (msg->state)
- input_state->buttons[user] |= 1 << msg->id;
- break;
- case RETRO_DEVICE_ANALOG:
- input_state->analog[msg->index * 2 + msg->id][user] = msg->state;
- break;
- }
- }
- #endif
- /* INPUT */
- void set_connection_listener(pad_connection_listener_t *listener)
- {
- struct rarch_state *p_rarch = &rarch_st;
- p_rarch->pad_connection_listener = listener;
- }
- /**
- * config_get_input_driver_options:
- *
- * Get an enumerated list of all input driver names, separated by '|'.
- *
- * Returns: string listing of all input driver names, separated by '|'.
- **/
- const char* config_get_input_driver_options(void)
- {
- return char_list_new_special(STRING_LIST_INPUT_DRIVERS, NULL);
- }
- /**
- * input_driver_set_rumble_state:
- * @port : User number.
- * @effect : Rumble effect.
- * @strength : Strength of rumble effect.
- *
- * Sets the rumble state.
- * Used by RETRO_ENVIRONMENT_GET_RUMBLE_INTERFACE.
- **/
- bool input_driver_set_rumble_state(unsigned port,
- enum retro_rumble_effect effect, uint16_t strength)
- {
- struct rarch_state *p_rarch = &rarch_st;
- settings_t *settings = p_rarch->configuration_settings;
- #ifdef HAVE_MFI
- const input_device_driver_t *sec_joypad = p_rarch->sec_joypad;
- #else
- const input_device_driver_t *sec_joypad = NULL;
- #endif
- bool rumble_state = false;
- unsigned joy_idx = settings->uints.input_joypad_map[port];
- if (joy_idx >= MAX_USERS)
- return false;
- if (p_rarch->joypad && p_rarch->joypad->set_rumble)
- rumble_state = p_rarch->joypad->set_rumble(
- joy_idx, effect, strength);
- if (sec_joypad && sec_joypad->set_rumble)
- rumble_state = sec_joypad->set_rumble(
- joy_idx, effect, strength);
- return rumble_state;
- }
- const char *joypad_driver_name(unsigned i)
- {
- struct rarch_state *p_rarch = &rarch_st;
- if (!p_rarch || !p_rarch->joypad || !p_rarch->joypad->name)
- return NULL;
- return p_rarch->joypad->name(i);
- }
- void joypad_driver_reinit(void *data, const char *joypad_driver_name)
- {
- struct rarch_state *p_rarch = &rarch_st;
- if (!p_rarch)
- return;
- if (p_rarch->joypad)
- p_rarch->joypad->destroy();
- p_rarch->joypad = NULL;
- #ifdef HAVE_MFI
- if (p_rarch->sec_joypad)
- p_rarch->sec_joypad->destroy();
- p_rarch->sec_joypad = NULL;
- #endif
- p_rarch->joypad = input_joypad_init_driver(joypad_driver_name, data);
- #ifdef HAVE_MFI
- p_rarch->sec_joypad = input_joypad_init_driver("mfi", data);
- #endif
- }
- static uint64_t input_driver_get_capabilities(void)
- {
- struct rarch_state *p_rarch = &rarch_st;
- if (!p_rarch->current_input || !p_rarch->current_input->get_capabilities)
- return 0;
- return p_rarch->current_input->get_capabilities(p_rarch->current_input_data);
- }
- /**
- * input_sensor_set_state:
- * @port : User number.
- * @effect : Sensor action.
- * @rate : Sensor rate update.
- *
- * Sets the sensor state.
- * Used by RETRO_ENVIRONMENT_GET_SENSOR_INTERFACE.
- **/
- bool input_sensor_set_state(unsigned port,
- enum retro_sensor_action action, unsigned rate)
- {
- struct rarch_state *p_rarch = &rarch_st;
- settings_t *settings = p_rarch->configuration_settings;
- bool input_sensors_enable = settings->bools.input_sensors_enable;
- /* If sensors are disabled, inhibit any enable
- * actions (but always allow disable actions) */
- if (!input_sensors_enable &&
- ((action == RETRO_SENSOR_ACCELEROMETER_ENABLE) ||
- (action == RETRO_SENSOR_GYROSCOPE_ENABLE) ||
- (action == RETRO_SENSOR_ILLUMINANCE_ENABLE)))
- return false;
- if (p_rarch->current_input_data &&
- p_rarch->current_input->set_sensor_state)
- return p_rarch->current_input->set_sensor_state(p_rarch->current_input_data,
- port, action, rate);
- return false;
- }
- float input_sensor_get_input(unsigned port, unsigned id)
- {
- struct rarch_state *p_rarch = &rarch_st;
- settings_t *settings = p_rarch->configuration_settings;
- bool input_sensors_enable = settings->bools.input_sensors_enable;
- if (input_sensors_enable &&
- p_rarch->current_input_data &&
- p_rarch->current_input->get_sensor_input)
- return p_rarch->current_input->get_sensor_input(p_rarch->current_input_data,
- port, id);
- return 0.0f;
- }
- /**
- * input_poll:
- *
- * Input polling callback function.
- **/
- static void input_driver_poll(void)
- {
- size_t i, j;
- rarch_joypad_info_t joypad_info[MAX_USERS];
- struct rarch_state *p_rarch = &rarch_st;
- settings_t *settings = p_rarch->configuration_settings;
- #ifdef HAVE_MFI
- const input_device_driver_t
- *sec_joypad = p_rarch->sec_joypad;
- #else
- const input_device_driver_t
- *sec_joypad = NULL;
- #endif
- #ifdef HAVE_OVERLAY
- float input_overlay_opacity = settings->floats.input_overlay_opacity;
- #endif
- bool input_remap_binds_enable = settings->bools.input_remap_binds_enable;
- uint8_t max_users = (uint8_t)p_rarch->input_driver_max_users;
- if ( p_rarch->joypad
- && p_rarch->joypad->poll)
- p_rarch->joypad->poll();
- #ifdef HAVE_MFI
- if ( p_rarch->sec_joypad
- && p_rarch->sec_joypad->poll)
- p_rarch->sec_joypad->poll();
- #endif
- if ( p_rarch->current_input
- && p_rarch->current_input->poll)
- p_rarch->current_input->poll(p_rarch->current_input_data);
- p_rarch->input_driver_turbo_btns.count++;
- if (p_rarch->input_driver_block_libretro_input)
- {
- for (i = 0; i < max_users; i++)
- p_rarch->input_driver_turbo_btns.frame_enable[i] = 0;
- return;
- }
- for (i = 0; i < max_users; i++)
- {
- joypad_info[i].axis_threshold = p_rarch->input_driver_axis_threshold;
- joypad_info[i].joy_idx = settings->uints.input_joypad_map[i];
- joypad_info[i].auto_binds = input_autoconf_binds[joypad_info[i].joy_idx];
- p_rarch->input_driver_turbo_btns.frame_enable[i] = p_rarch->libretro_input_binds[i][RARCH_TURBO_ENABLE].valid ?
- input_state_wrap(
- p_rarch->current_input,
- p_rarch->current_input_data,
- p_rarch->joypad,
- sec_joypad,
- &joypad_info[i],
- p_rarch->libretro_input_binds,
- p_rarch->keyboard_mapping_blocked,
- (unsigned)i,
- RETRO_DEVICE_JOYPAD,
- 0,
- RARCH_TURBO_ENABLE) : 0;
- }
- #ifdef HAVE_OVERLAY
- if (p_rarch->overlay_ptr && p_rarch->overlay_ptr->alive)
- input_poll_overlay(p_rarch,
- p_rarch->overlay_ptr,
- input_overlay_opacity,
- settings->uints.input_analog_dpad_mode[0],
- p_rarch->input_driver_axis_threshold);
- #endif
- #ifdef HAVE_MENU
- if (!p_rarch->menu_driver_alive)
- #endif
- if (input_remap_binds_enable)
- {
- #ifdef HAVE_OVERLAY
- input_overlay_t *overlay_pointer = (input_overlay_t*)p_rarch->overlay_ptr;
- bool poll_overlay = (p_rarch->overlay_ptr && p_rarch->overlay_ptr->alive);
- #endif
- input_mapper_t *handle = p_rarch->input_driver_mapper;
- const input_device_driver_t *joypad_driver
- = p_rarch->joypad;
- for (i = 0; i < max_users; i++)
- {
- input_bits_t current_inputs;
- unsigned device
- = settings->uints.input_libretro_device[i]
- & RETRO_DEVICE_MASK;
- input_bits_t *p_new_state
- = (input_bits_t*)¤t_inputs;
- switch (device)
- {
- case RETRO_DEVICE_KEYBOARD:
- case RETRO_DEVICE_JOYPAD:
- case RETRO_DEVICE_ANALOG:
- BIT256_CLEAR_ALL_PTR(¤t_inputs);
- if (joypad_driver)
- {
- unsigned k, j;
- int16_t ret = input_state_wrap(
- p_rarch->current_input,
- p_rarch->current_input_data,
- p_rarch->joypad,
- sec_joypad,
- &joypad_info[i],
- p_rarch->libretro_input_binds,
- p_rarch->keyboard_mapping_blocked,
- (unsigned)i, RETRO_DEVICE_JOYPAD,
- 0, RETRO_DEVICE_ID_JOYPAD_MASK);
- for (k = 0; k < RARCH_FIRST_CUSTOM_BIND; k++)
- {
- if (ret & (1 << k))
- {
- bool valid_bind =
- p_rarch->libretro_input_binds[i][k].valid;
- if (valid_bind)
- {
- int16_t val =
- input_joypad_analog_button(
- p_rarch, settings,
- joypad_driver, &joypad_info[i], (unsigned)i,
- RETRO_DEVICE_INDEX_ANALOG_BUTTON, k,
- p_rarch->libretro_input_binds[i]);
- if (val)
- p_new_state->analog_buttons[k] = val;
- }
- BIT256_SET_PTR(p_new_state, k);
- }
- }
- /* This is the analog joypad index -
- * handles only the two analog axes */
- for (k = 0; k < 2; k++)
- {
- /* This is the analog joypad ident */
- for (j = 0; j < 2; j++)
- {
- unsigned offset = 0 + (k * 4) + (j * 2);
- int16_t val = input_joypad_analog_axis(
- p_rarch,
- settings,
- joypad_driver,
- &joypad_info[i], (unsigned)i, k, j,
- p_rarch->libretro_input_binds[i]);
- if (val >= 0)
- p_new_state->analogs[offset] = val;
- else
- p_new_state->analogs[offset+1] = val;
- }
- }
- }
- break;
- default:
- break;
- }
- /* mapper */
- switch (device)
- {
- /* keyboard to gamepad remapping */
- case RETRO_DEVICE_KEYBOARD:
- for (j = 0; j < RARCH_CUSTOM_BIND_LIST_END; j++)
- {
- unsigned current_button_value;
- unsigned remap_key =
- settings->uints.input_keymapper_ids[i][j];
- if (remap_key == RETROK_UNKNOWN)
- continue;
- current_button_value =
- BIT256_GET_PTR(p_new_state, j);
- #ifdef HAVE_OVERLAY
- if (poll_overlay && i == 0)
- {
- input_overlay_state_t *ol_state =
- overlay_pointer
- ? &overlay_pointer->overlay_state
- : NULL;
- if (ol_state)
- current_button_value |=
- BIT256_GET(ol_state->buttons, j);
- }
- #endif
- /* Press */
- if ((current_button_value == 1)
- && !MAPPER_GET_KEY(handle, remap_key))
- {
- handle->key_button[remap_key] = (unsigned)j;
- MAPPER_SET_KEY(handle, remap_key);
- input_keyboard_event(true,
- remap_key,
- 0, 0, RETRO_DEVICE_KEYBOARD);
- }
- /* Release */
- else if ((current_button_value == 0)
- && MAPPER_GET_KEY(handle, remap_key))
- {
- if (handle->key_button[remap_key] != j)
- continue;
- input_keyboard_event(false,
- remap_key,
- 0, 0, RETRO_DEVICE_KEYBOARD);
- MAPPER_UNSET_KEY(handle, remap_key);
- }
- }
- break;
- /* gamepad remapping */
- case RETRO_DEVICE_JOYPAD:
- case RETRO_DEVICE_ANALOG:
- /* this loop iterates on all users and all buttons,
- * and checks if a pressed button is assigned to any
- * other button than the default one, then it sets
- * the bit on the mapper input bitmap, later on the
- * original input is cleared in input_state */
- BIT256_CLEAR_ALL(handle->buttons[i]);
- for (j = 0; j < 8; j++)
- handle->analog_value[i][j] = 0;
- for (j = 0; j < RARCH_FIRST_CUSTOM_BIND; j++)
- {
- bool remap_valid;
- unsigned remap_button =
- settings->uints.input_remap_ids[i][j];
- unsigned current_button_value =
- BIT256_GET_PTR(p_new_state, j);
- #ifdef HAVE_OVERLAY
- if (poll_overlay && i == 0)
- {
- input_overlay_state_t *ol_state =
- overlay_pointer
- ? &overlay_pointer->overlay_state
- : NULL;
- if (ol_state)
- current_button_value |=
- BIT256_GET(ol_state->buttons, j);
- }
- #endif
- remap_valid =
- (current_button_value == 1) &&
- (j != remap_button) &&
- (remap_button != RARCH_UNMAPPED);
- #ifdef HAVE_ACCESSIBILITY
- /* gamepad override */
- if (i == 0 &&
- p_rarch->gamepad_input_override & (1 << j))
- {
- BIT256_SET(handle->buttons[i], j);
- }
- #endif
- if (remap_valid)
- {
- if (remap_button < RARCH_FIRST_CUSTOM_BIND)
- {
- BIT256_SET(handle->buttons[i], remap_button);
- }
- else
- {
- int invert = 1;
- if (remap_button % 2 != 0)
- invert = -1;
- handle->analog_value[i][
- remap_button - RARCH_FIRST_CUSTOM_BIND] =
- (p_new_state->analog_buttons[j]
- ? p_new_state->analog_buttons[j]
- : 32767) * invert;
- }
- }
- }
- for (j = 0; j < 8; j++)
- {
- unsigned k = (unsigned)j + RARCH_FIRST_CUSTOM_BIND;
- int16_t current_axis_value = p_new_state->analogs[j];
- unsigned remap_axis =
- settings->uints.input_remap_ids[i][k];
- if (
- (abs(current_axis_value) > 0 &&
- (k != remap_axis) &&
- (remap_axis != RARCH_UNMAPPED)
- ))
- {
- if (remap_axis < RARCH_FIRST_CUSTOM_BIND &&
- abs(current_axis_value) >
- p_rarch->input_driver_axis_threshold
- * 32767)
- {
- BIT256_SET(handle->buttons[i], remap_axis);
- }
- else
- {
- unsigned remap_axis_bind =
- remap_axis - RARCH_FIRST_CUSTOM_BIND;
- if (remap_axis_bind < sizeof(handle->analog_value[i]))
- {
- int invert = 1;
- if ( (k % 2 == 0 && remap_axis % 2 != 0) ||
- (k % 2 != 0 && remap_axis % 2 == 0)
- )
- invert = -1;
- handle->analog_value[i][
- remap_axis_bind] =
- current_axis_value * invert;
- }
- }
- }
- }
- break;
- default:
- break;
- }
- }
- }
- #ifdef HAVE_COMMAND
- if (p_rarch->input_driver_command)
- {
- memset(p_rarch->input_driver_command->state,
- 0, sizeof(p_rarch->input_driver_command->state));
- #if defined(HAVE_NETWORK_CMD) && defined(HAVE_COMMAND)
- command_network_poll(p_rarch,
- p_rarch->input_driver_command);
- #endif
- #ifdef HAVE_STDIN_CMD
- if (p_rarch->input_driver_command->stdin_enable)
- command_stdin_poll(p_rarch,
- p_rarch->input_driver_command);
- #endif
- }
- #endif
- #ifdef HAVE_NETWORKGAMEPAD
- /* Poll remote */
- if (p_rarch->input_driver_remote)
- {
- unsigned user;
- for (user = 0; user < max_users; user++)
- {
- if (settings->bools.network_remote_enable_user[user])
- {
- #if defined(HAVE_NETWORKING) && defined(HAVE_NETWORKGAMEPAD)
- fd_set fds;
- ssize_t ret;
- struct remote_message msg;
- if (p_rarch->input_driver_remote->net_fd[user] < 0)
- return;
- FD_ZERO(&fds);
- FD_SET(p_rarch->input_driver_remote->net_fd[user], &fds);
- ret = recvfrom(p_rarch->input_driver_remote->net_fd[user],
- (char*)&msg,
- sizeof(msg), 0, NULL, NULL);
- if (ret == sizeof(msg))
- input_remote_parse_packet(&p_rarch->remote_st_ptr, &msg, user);
- else if ((ret != -1) || ((errno != EAGAIN) && (errno != ENOENT)))
- #endif
- {
- input_remote_state_t *input_state = &p_rarch->remote_st_ptr;
- input_state->buttons[user] = 0;
- input_state->analog[0][user] = 0;
- input_state->analog[1][user] = 0;
- input_state->analog[2][user] = 0;
- input_state->analog[3][user] = 0;
- }
- }
- }
- }
- #endif
- }
- static int16_t input_state_device(
- struct rarch_state *p_rarch,
- int16_t ret,
- unsigned port, unsigned device,
- unsigned idx, unsigned id,
- bool button_mask)
- {
- int16_t res = 0;
- settings_t *settings = p_rarch->configuration_settings;
- bool input_remap_binds_enable = settings->bools.input_remap_binds_enable;
- switch (device)
- {
- case RETRO_DEVICE_JOYPAD:
- if (id < RARCH_FIRST_META_KEY)
- {
- #ifdef HAVE_NETWORKGAMEPAD
- /* Don't process binds if input is coming from Remote RetroPad */
- if ( p_rarch->input_driver_remote
- && INPUT_REMOTE_KEY_PRESSED(p_rarch, id, port))
- res |= 1;
- else
- #endif
- {
- if (input_remap_binds_enable)
- {
- bool bind_valid = p_rarch->libretro_input_binds[port]
- && p_rarch->libretro_input_binds[port][id].valid;
- if (!
- ( bind_valid
- && id != settings->uints.input_remap_ids[port][id]
- )
- )
- {
- if (button_mask)
- {
- if (ret & (1 << id))
- res |= (1 << id);
- }
- else
- res = ret;
- }
- if (BIT256_GET(p_rarch->input_driver_mapper->buttons[port], id))
- res = 1;
- }
- #ifdef HAVE_OVERLAY
- if (port == 0)
- {
- if (p_rarch->overlay_ptr && p_rarch->overlay_ptr->alive)
- if ((BIT256_GET(p_rarch->overlay_ptr->overlay_state.buttons, id)))
- res |= 1;
- }
- #endif
- }
- }
- /* Don't allow turbo for D-pad. */
- if ( (id < RETRO_DEVICE_ID_JOYPAD_UP) ||
- ( (id > RETRO_DEVICE_ID_JOYPAD_RIGHT) &&
- (id <= RETRO_DEVICE_ID_JOYPAD_R3)))
- {
- /*
- * Apply turbo button if activated.
- */
- unsigned turbo_mode = settings->uints.input_turbo_mode;
- if (turbo_mode > INPUT_TURBO_MODE_CLASSIC)
- {
- /* Pressing turbo button toggles turbo mode on or off.
- * Holding the button will
- * pass through, else the pressed state will be modulated by a
- * periodic pulse defined by the configured duty cycle.
- */
- /* Avoid detecting the turbo button being held as multiple toggles */
- if (!p_rarch->input_driver_turbo_btns.frame_enable[port])
- p_rarch->input_driver_turbo_btns.turbo_pressed[port] &= ~(1 << 31);
- else if (p_rarch->input_driver_turbo_btns.turbo_pressed[port]>=0)
- {
- p_rarch->input_driver_turbo_btns.turbo_pressed[port] |= (1 << 31);
- /* Toggle turbo for selected buttons. */
- if (p_rarch->input_driver_turbo_btns.enable[port]
- != (1 << settings->uints.input_turbo_default_button))
- {
- static const int button_map[]={
- RETRO_DEVICE_ID_JOYPAD_B,
- RETRO_DEVICE_ID_JOYPAD_Y,
- RETRO_DEVICE_ID_JOYPAD_A,
- RETRO_DEVICE_ID_JOYPAD_X,
- RETRO_DEVICE_ID_JOYPAD_L,
- RETRO_DEVICE_ID_JOYPAD_R,
- RETRO_DEVICE_ID_JOYPAD_L2,
- RETRO_DEVICE_ID_JOYPAD_R2,
- RETRO_DEVICE_ID_JOYPAD_L3,
- RETRO_DEVICE_ID_JOYPAD_R3};
- p_rarch->input_driver_turbo_btns.enable[port] = 1 << button_map[
- MIN(
- sizeof(button_map)/sizeof(button_map[0])-1,
- settings->uints.input_turbo_default_button)];
- }
- p_rarch->input_driver_turbo_btns.mode1_enable[port] ^= 1;
- }
- if (p_rarch->input_driver_turbo_btns.turbo_pressed[port] & (1 << 31))
- {
- /* Avoid detecting buttons being held as multiple toggles */
- if (!res)
- p_rarch->input_driver_turbo_btns.turbo_pressed[port] &= ~(1 << id);
- else if (!(p_rarch->input_driver_turbo_btns.turbo_pressed[port] & (1 << id)) &&
- turbo_mode == INPUT_TURBO_MODE_SINGLEBUTTON)
- {
- uint16_t enable_new;
- p_rarch->input_driver_turbo_btns.turbo_pressed[port] |= 1 << id;
- /* Toggle turbo for pressed button but make
- * sure at least one button has turbo */
- enable_new = p_rarch->input_driver_turbo_btns.enable[port] ^ (1 << id);
- if (enable_new)
- p_rarch->input_driver_turbo_btns.enable[port] = enable_new;
- }
- }
- else if (turbo_mode == INPUT_TURBO_MODE_SINGLEBUTTON_HOLD &&
- p_rarch->input_driver_turbo_btns.enable[port] &&
- p_rarch->input_driver_turbo_btns.mode1_enable[port])
- {
- /* Hold mode stops turbo on release */
- p_rarch->input_driver_turbo_btns.mode1_enable[port] = 0;
- }
- if (!res && p_rarch->input_driver_turbo_btns.mode1_enable[port] &&
- p_rarch->input_driver_turbo_btns.enable[port] & (1 << id))
- {
- /* if turbo button is enabled for this key ID */
- res = ((p_rarch->input_driver_turbo_btns.count
- % settings->uints.input_turbo_period)
- < settings->uints.input_turbo_duty_cycle);
- }
- }
- else
- {
- /* If turbo button is held, all buttons pressed except
- * for D-pad will go into a turbo mode. Until the button is
- * released again, the input state will be modulated by a
- * periodic pulse defined by the configured duty cycle.
- */
- if (res)
- {
- if (p_rarch->input_driver_turbo_btns.frame_enable[port])
- p_rarch->input_driver_turbo_btns.enable[port] |= (1 << id);
- if (p_rarch->input_driver_turbo_btns.enable[port] & (1 << id))
- /* if turbo button is enabled for this key ID */
- res = ((p_rarch->input_driver_turbo_btns.count
- % settings->uints.input_turbo_period)
- < settings->uints.input_turbo_duty_cycle);
- }
- else
- p_rarch->input_driver_turbo_btns.enable[port] &= ~(1 << id);
- }
- }
- break;
- case RETRO_DEVICE_KEYBOARD:
- res = ret;
- if (id < RETROK_LAST)
- {
- #ifdef HAVE_OVERLAY
- if (port == 0)
- {
- if (p_rarch->overlay_ptr && p_rarch->overlay_ptr->alive)
- {
- input_overlay_state_t
- *ol_state = &p_rarch->overlay_ptr->overlay_state;
- if (OVERLAY_GET_KEY(ol_state, id))
- res |= 1;
- }
- }
- #endif
- if (input_remap_binds_enable)
- if (MAPPER_GET_KEY(p_rarch->input_driver_mapper, id))
- res |= 1;
- }
- break;
- case RETRO_DEVICE_ANALOG:
- {
- #if defined(HAVE_NETWORKGAMEPAD) || defined(HAVE_OVERLAY)
- #ifdef HAVE_NETWORKGAMEPAD
- input_remote_state_t
- *input_state = &p_rarch->remote_st_ptr;
- #endif
- unsigned base = (idx == RETRO_DEVICE_INDEX_ANALOG_RIGHT)
- ? 2 : 0;
- if (id == RETRO_DEVICE_ID_ANALOG_Y)
- base += 1;
- #ifdef HAVE_NETWORKGAMEPAD
- if (p_rarch->input_driver_remote
- && input_state && input_state->analog[base][port])
- res = input_state->analog[base][port];
- else
- #endif
- #endif
- {
- if (id < RARCH_FIRST_META_KEY)
- {
- bool bind_valid = p_rarch->libretro_input_binds[port]
- && p_rarch->libretro_input_binds[port][id].valid;
- if (bind_valid)
- {
- /* reset_state - used to reset input state of a button
- * when the gamepad mapper is in action for that button*/
- bool reset_state = false;
- if (input_remap_binds_enable)
- {
- if (idx < 2 && id < 2)
- {
- unsigned offset = RARCH_FIRST_CUSTOM_BIND +
- (idx * 4) + (id * 2);
- if (settings->uints.input_remap_ids
- [port][offset] != offset)
- reset_state = true;
- else if (settings->uints.input_remap_ids
- [port][offset+1] != (offset+1))
- reset_state = true;
- }
- }
- if (reset_state)
- res = 0;
- else
- {
- res = ret;
- #ifdef HAVE_OVERLAY
- if ( p_rarch->overlay_ptr &&
- p_rarch->overlay_ptr->alive && port == 0)
- {
- input_overlay_state_t *ol_state =
- &p_rarch->overlay_ptr->overlay_state;
- if (ol_state->analog[base])
- res |= ol_state->analog[base];
- }
- #endif
- }
- }
- }
- }
- if (input_remap_binds_enable)
- {
- if (idx < 2 && id < 2)
- {
- unsigned offset = 0 + (idx * 4) + (id * 2);
- int val1 = p_rarch->input_driver_mapper->analog_value[port][offset];
- int val2 = p_rarch->input_driver_mapper->analog_value[port][offset+1];
- if (val1)
- res |= val1;
- else if (val2)
- res |= val2;
- }
- }
- }
- break;
- case RETRO_DEVICE_MOUSE:
- case RETRO_DEVICE_LIGHTGUN:
- case RETRO_DEVICE_POINTER:
- if (id < RARCH_FIRST_META_KEY)
- {
- bool bind_valid = p_rarch->libretro_input_binds[port]
- && p_rarch->libretro_input_binds[port][id].valid;
- if (bind_valid)
- {
- if (button_mask)
- {
- if (ret & (1 << id))
- res |= (1 << id);
- }
- else
- res = ret;
- }
- }
- break;
- }
- return res;
- }
- /**
- * input_state:
- * @port : user number.
- * @device : device identifier of user.
- * @idx : index value of user.
- * @id : identifier of key pressed by user.
- *
- * Input state callback function.
- *
- * Returns: Non-zero if the given key (identified by @id)
- * was pressed by the user (assigned to @port).
- **/
- static int16_t input_state(unsigned port, unsigned device,
- unsigned idx, unsigned id)
- {
- rarch_joypad_info_t joypad_info;
- struct rarch_state *p_rarch = &rarch_st;
- settings_t *settings = p_rarch->configuration_settings;
- int16_t result = 0;
- int16_t ret = 0;
- #ifdef HAVE_MFI
- const input_device_driver_t
- *sec_joypad = p_rarch->sec_joypad;
- #else
- const input_device_driver_t
- *sec_joypad = NULL;
- #endif
- joypad_info.axis_threshold = p_rarch->input_driver_axis_threshold;
- joypad_info.joy_idx = settings->uints.input_joypad_map[port];
- joypad_info.auto_binds = input_autoconf_binds[joypad_info.joy_idx];
- #ifdef HAVE_BSV_MOVIE
- if (BSV_MOVIE_IS_PLAYBACK_ON())
- {
- int16_t bsv_result;
- if (intfstream_read(p_rarch->bsv_movie_state_handle->file, &bsv_result, 2) == 2)
- {
- #ifdef HAVE_CHEEVOS
- rcheevos_pause_hardcore();
- #endif
- return swap_if_big16(bsv_result);
- }
- p_rarch->bsv_movie_state.movie_end = true;
- }
- #endif
- device &= RETRO_DEVICE_MASK;
- ret = input_state_wrap(
- p_rarch->current_input,
- p_rarch->current_input_data,
- p_rarch->joypad,
- sec_joypad,
- &joypad_info,
- p_rarch->libretro_input_binds,
- p_rarch->keyboard_mapping_blocked,
- port, device, idx, id);
- if ( (device == RETRO_DEVICE_ANALOG) &&
- (ret == 0))
- {
- const input_device_driver_t *joypad = p_rarch->joypad;
- #ifdef HAVE_MFI
- const input_device_driver_t *sec_joypad = p_rarch->sec_joypad;
- #else
- const input_device_driver_t *sec_joypad = NULL;
- #endif
- if (p_rarch->libretro_input_binds[port])
- {
- if (idx == RETRO_DEVICE_INDEX_ANALOG_BUTTON)
- {
- if (id < RARCH_FIRST_CUSTOM_BIND)
- {
- bool valid_bind =
- p_rarch->libretro_input_binds[port][id].valid;
- if (valid_bind)
- {
- if (sec_joypad)
- ret =
- input_joypad_analog_button(
- p_rarch, settings,
- sec_joypad, &joypad_info,
- port, idx, id, p_rarch->libretro_input_binds[port]);
- if (joypad && (ret == 0))
- ret = input_joypad_analog_button(
- p_rarch, settings,
- joypad, &joypad_info,
- port, idx, id, p_rarch->libretro_input_binds[port]);
- }
- }
- }
- else
- {
- if (sec_joypad)
- ret = input_joypad_analog_axis(p_rarch, settings,
- sec_joypad, &joypad_info,
- port, idx, id, p_rarch->libretro_input_binds[port]);
- if (joypad && (ret == 0))
- ret = input_joypad_analog_axis(p_rarch, settings,
- joypad, &joypad_info,
- port, idx, id, p_rarch->libretro_input_binds[port]);
- }
- }
- }
- if ( (p_rarch->input_driver_flushing_input == 0)
- && !p_rarch->input_driver_block_libretro_input)
- {
- if ( (device == RETRO_DEVICE_JOYPAD) &&
- (id == RETRO_DEVICE_ID_JOYPAD_MASK))
- {
- unsigned i;
- {
- for (i = 0; i < RARCH_FIRST_CUSTOM_BIND; i++)
- if (input_state_device(p_rarch, ret, port, device, idx, i, true))
- result |= (1 << i);
- }
- }
- else
- result = input_state_device(p_rarch, ret, port, device, idx, id, false);
- }
- #ifdef HAVE_BSV_MOVIE
- if (BSV_MOVIE_IS_PLAYBACK_OFF())
- {
- result = swap_if_big16(result);
- intfstream_write(p_rarch->bsv_movie_state_handle->file, &result, 2);
- }
- #endif
- return result;
- }
- static int16_t input_joypad_axis(
- struct rarch_state *p_rarch,
- const input_device_driver_t *drv,
- unsigned port, uint32_t joyaxis, float normal_mag)
- {
- settings_t *settings = p_rarch->configuration_settings;
- float input_analog_deadzone = settings->floats.input_analog_deadzone;
- float input_analog_sensitivity = settings->floats.input_analog_sensitivity;
- int16_t val = (joyaxis != AXIS_NONE) ? drv->axis(port, joyaxis) : 0;
- if (input_analog_deadzone)
- {
- /* if analog value is below the deadzone, ignore it
- * normal magnitude is calculated radially for analog sticks
- * and linearly for analog buttons */
- if (normal_mag <= input_analog_deadzone)
- return 0;
- /* due to the way normal_mag is calculated differently for buttons and
- * sticks, this results in either a radial scaled deadzone for sticks
- * or linear scaled deadzone for analog buttons */
- val = val * MAX(1.0f,(1.0f / normal_mag)) * MIN(1.0f,((normal_mag - input_analog_deadzone)
- / (1.0f - input_analog_deadzone)));
- }
- if (input_analog_sensitivity != 1.0f)
- {
- float normalized = (1.0f / 0x7fff) * val;
- int new_val = 0x7fff * normalized *
- input_analog_sensitivity;
- if (new_val > 0x7fff)
- return 0x7fff;
- else if (new_val < -0x7fff)
- return -0x7fff;
- return new_val;
- }
- return val;
- }
- /* MENU INPUT */
- #ifdef HAVE_MENU
- /* Must be called inside menu_driver_toggle()
- * Prevents phantom input when using an overlay to
- * toggle menu ON if overlays are disabled in-menu */
- static void menu_input_driver_toggle(
- struct rarch_state *p_rarch,
- menu_input_t *menu_input,
- settings_t *settings,
- bool on)
- {
- #ifdef HAVE_OVERLAY
- if (on)
- {
- bool overlay_hide_in_menu = settings->bools.input_overlay_hide_in_menu;
- bool input_overlay_enable = settings->bools.input_overlay_enable;
- /* If an overlay was displayed before the toggle
- * and overlays are disabled in menu, need to
- * inhibit 'select' input */
- if (overlay_hide_in_menu)
- if ( input_overlay_enable &&
- p_rarch->overlay_ptr &&
- p_rarch->overlay_ptr->alive)
- {
- /* Inhibits pointer 'select' and 'cancel' actions
- * (until the next time 'select'/'cancel' are released) */
- menu_input->select_inhibit = true;
- menu_input->cancel_inhibit = true;
- }
- }
- else
- #endif
- {
- /* Inhibits pointer 'select' and 'cancel' actions
- * (until the next time 'select'/'cancel' are released) */
- menu_input->select_inhibit = false;
- menu_input->cancel_inhibit = false;
- }
- }
- static int16_t menu_input_read_mouse_hw(
- struct rarch_state *p_rarch,
- enum menu_input_mouse_hw_id id)
- {
- rarch_joypad_info_t joypad_info;
- unsigned type = 0;
- unsigned device = RETRO_DEVICE_MOUSE;
- input_driver_t *current_input = p_rarch->current_input;
- #ifdef HAVE_MFI
- const input_device_driver_t
- *sec_joypad = p_rarch->sec_joypad;
- #else
- const input_device_driver_t
- *sec_joypad = NULL;
- #endif
- joypad_info.joy_idx = 0;
- joypad_info.auto_binds = NULL;
- joypad_info.axis_threshold = 0.0f;
- switch (id)
- {
- case MENU_MOUSE_X_AXIS:
- device = RARCH_DEVICE_MOUSE_SCREEN;
- type = RETRO_DEVICE_ID_MOUSE_X;
- break;
- case MENU_MOUSE_Y_AXIS:
- device = RARCH_DEVICE_MOUSE_SCREEN;
- type = RETRO_DEVICE_ID_MOUSE_Y;
- break;
- case MENU_MOUSE_LEFT_BUTTON:
- type = RETRO_DEVICE_ID_MOUSE_LEFT;
- break;
- case MENU_MOUSE_RIGHT_BUTTON:
- type = RETRO_DEVICE_ID_MOUSE_RIGHT;
- break;
- case MENU_MOUSE_WHEEL_UP:
- type = RETRO_DEVICE_ID_MOUSE_WHEELUP;
- break;
- case MENU_MOUSE_WHEEL_DOWN:
- type = RETRO_DEVICE_ID_MOUSE_WHEELDOWN;
- break;
- case MENU_MOUSE_HORIZ_WHEEL_UP:
- type = RETRO_DEVICE_ID_MOUSE_HORIZ_WHEELUP;
- break;
- case MENU_MOUSE_HORIZ_WHEEL_DOWN:
- type = RETRO_DEVICE_ID_MOUSE_HORIZ_WHEELDOWN;
- break;
- }
- if (!current_input->input_state)
- return 0;
- return current_input->input_state(
- p_rarch->current_input_data,
- p_rarch->joypad,
- sec_joypad,
- &joypad_info,
- NULL,
- p_rarch->keyboard_mapping_blocked,
- 0, device, 0, type);
- }
- static void menu_input_get_mouse_hw_state(
- struct rarch_state *p_rarch,
- menu_input_pointer_hw_state_t *hw_state)
- {
- settings_t *settings = p_rarch->configuration_settings;
- static int16_t last_x = 0;
- static int16_t last_y = 0;
- static bool last_select_pressed = false;
- static bool last_cancel_pressed = false;
- bool mouse_enabled = settings->bools.menu_mouse_enable;
- menu_handle_t *menu = p_rarch->menu_driver_data;
- bool menu_has_fb =
- (menu &&
- menu->driver_ctx &&
- menu->driver_ctx->set_texture);
- #ifdef HAVE_OVERLAY
- bool overlay_enable = settings->bools.input_overlay_enable;
- /* Menu pointer controls are ignored when overlays are enabled. */
- bool overlay_active = overlay_enable && p_rarch->overlay_ptr
- && p_rarch->overlay_ptr->alive;
- if (overlay_active)
- mouse_enabled = false;
- #endif
- /* Easiest to set inactive by default, and toggle
- * when input is detected */
- hw_state->active = false;
- if (!mouse_enabled)
- {
- hw_state->x = 0;
- hw_state->y = 0;
- hw_state->select_pressed = false;
- hw_state->cancel_pressed = false;
- hw_state->up_pressed = false;
- hw_state->down_pressed = false;
- hw_state->left_pressed = false;
- hw_state->right_pressed = false;
- return;
- }
- /* X pos */
- hw_state->x = menu_input_read_mouse_hw(p_rarch, MENU_MOUSE_X_AXIS);
- if (hw_state->x != last_x)
- hw_state->active = true;
- last_x = hw_state->x;
- /* Y pos */
- hw_state->y = menu_input_read_mouse_hw(p_rarch, MENU_MOUSE_Y_AXIS);
- if (hw_state->y != last_y)
- hw_state->active = true;
- last_y = hw_state->y;
- /* > X/Y adjustment */
- if (menu_has_fb)
- {
- /* RGUI uses a framebuffer texture + custom viewports,
- * which means we have to convert from screen space to
- * menu space... */
- size_t fb_pitch;
- unsigned fb_width, fb_height;
- struct video_viewport vp = {0};
- /* Read display/framebuffer info */
- gfx_display_get_fb_size(&fb_width, &fb_height, &fb_pitch);
- video_driver_get_viewport_info(&vp);
- /* Adjust X pos */
- hw_state->x = (int16_t)(((float)(hw_state->x - vp.x) / (float)vp.width) * (float)fb_width);
- hw_state->x = hw_state->x < 0 ? 0 : hw_state->x;
- hw_state->x = hw_state->x >= fb_width ? fb_width - 1 : hw_state->x;
- /* Adjust Y pos */
- hw_state->y = (int16_t)(((float)(hw_state->y - vp.y) / (float)vp.height) * (float)fb_height);
- hw_state->y = hw_state->y < 0 ? 0 : hw_state->y;
- hw_state->y = hw_state->y >= fb_height ? fb_height - 1 : hw_state->y;
- }
- /* Select (LMB)
- * Note that releasing select also counts as activity */
- hw_state->select_pressed = (bool)
- menu_input_read_mouse_hw(p_rarch, MENU_MOUSE_LEFT_BUTTON);
- if (hw_state->select_pressed || (hw_state->select_pressed != last_select_pressed))
- hw_state->active = true;
- last_select_pressed = hw_state->select_pressed;
- /* Cancel (RMB)
- * Note that releasing cancel also counts as activity */
- hw_state->cancel_pressed = (bool)
- menu_input_read_mouse_hw(p_rarch, MENU_MOUSE_RIGHT_BUTTON);
- if (hw_state->cancel_pressed || (hw_state->cancel_pressed != last_cancel_pressed))
- hw_state->active = true;
- last_cancel_pressed = hw_state->cancel_pressed;
- /* Up (mouse wheel up) */
- hw_state->up_pressed = (bool)
- menu_input_read_mouse_hw(p_rarch, MENU_MOUSE_WHEEL_UP);
- if (hw_state->up_pressed)
- hw_state->active = true;
- /* Down (mouse wheel down) */
- hw_state->down_pressed = (bool)
- menu_input_read_mouse_hw(p_rarch, MENU_MOUSE_WHEEL_DOWN);
- if (hw_state->down_pressed)
- hw_state->active = true;
- /* Left (mouse wheel horizontal left) */
- hw_state->left_pressed = (bool)
- menu_input_read_mouse_hw(p_rarch, MENU_MOUSE_HORIZ_WHEEL_DOWN);
- if (hw_state->left_pressed)
- hw_state->active = true;
- /* Right (mouse wheel horizontal right) */
- hw_state->right_pressed = (bool)
- menu_input_read_mouse_hw(p_rarch, MENU_MOUSE_HORIZ_WHEEL_UP);
- if (hw_state->right_pressed)
- hw_state->active = true;
- }
- static void menu_input_get_touchscreen_hw_state(
- struct rarch_state *p_rarch,
- menu_input_pointer_hw_state_t *hw_state)
- {
- rarch_joypad_info_t joypad_info;
- size_t fb_pitch;
- unsigned fb_width, fb_height;
- int pointer_x = 0;
- int pointer_y = 0;
- settings_t *settings =
- p_rarch->configuration_settings;
- const struct retro_keybind *binds[MAX_USERS] = {NULL};
- input_driver_t *current_input = p_rarch->current_input;
- menu_handle_t *menu = p_rarch->menu_driver_data;
- /* Is a background texture set for the current menu driver?
- * Checks if the menu framebuffer is set.
- * This would usually only return true
- * for framebuffer-based menu drivers, like RGUI. */
- int pointer_device =
- (menu && menu->driver_ctx && menu->driver_ctx->set_texture) ?
- RETRO_DEVICE_POINTER : RARCH_DEVICE_POINTER_SCREEN;
- static int16_t last_x = 0;
- static int16_t last_y = 0;
- static bool last_select_pressed = false;
- static bool last_cancel_pressed = false;
- bool overlay_active = false;
- bool pointer_enabled = settings->bools.menu_pointer_enable;
- #ifdef HAVE_MFI
- const input_device_driver_t
- *sec_joypad = p_rarch->sec_joypad;
- #else
- const input_device_driver_t
- *sec_joypad = NULL;
- #endif
- /* Easiest to set inactive by default, and toggle
- * when input is detected */
- hw_state->active = false;
- /* Touch screens don't have mouse wheels, so these
- * are always disabled */
- hw_state->up_pressed = false;
- hw_state->down_pressed = false;
- hw_state->left_pressed = false;
- hw_state->right_pressed = false;
- #ifdef HAVE_OVERLAY
- /* Menu pointer controls are ignored when overlays are enabled. */
- overlay_active = settings->bools.input_overlay_enable
- && p_rarch->overlay_ptr && p_rarch->overlay_ptr->alive;
- if (overlay_active)
- pointer_enabled = false;
- #endif
- /* If touchscreen is disabled, ignore all input */
- if (!pointer_enabled)
- {
- hw_state->x = 0;
- hw_state->y = 0;
- hw_state->select_pressed = false;
- hw_state->cancel_pressed = false;
- return;
- }
- /* TODO/FIXME - this should only be used for framebuffer-based
- * menu drivers like RGUI. Touchscreen input as a whole should
- * NOT be dependent on this
- */
- gfx_display_get_fb_size(&fb_width, &fb_height, &fb_pitch);
- joypad_info.joy_idx = 0;
- joypad_info.auto_binds = NULL;
- joypad_info.axis_threshold = 0.0f;
- /* X pos */
- if (current_input->input_state)
- pointer_x = current_input->input_state(
- p_rarch->current_input_data,
- p_rarch->joypad,
- sec_joypad,
- &joypad_info, binds,
- p_rarch->keyboard_mapping_blocked,
- 0, pointer_device,
- 0, RETRO_DEVICE_ID_POINTER_X);
- hw_state->x = ((pointer_x + 0x7fff) * (int)fb_width) / 0xFFFF;
- /* > An annoyance - we get different starting positions
- * depending upon whether pointer_device is
- * RETRO_DEVICE_POINTER or RARCH_DEVICE_POINTER_SCREEN,
- * so different 'activity' checks are required to prevent
- * false positives on first run */
- if (pointer_device == RARCH_DEVICE_POINTER_SCREEN)
- {
- if (hw_state->x != last_x)
- hw_state->active = true;
- last_x = hw_state->x;
- }
- else
- {
- if (pointer_x != last_x)
- hw_state->active = true;
- last_x = pointer_x;
- }
- /* Y pos */
- if (current_input->input_state)
- pointer_y = current_input->input_state(
- p_rarch->current_input_data,
- p_rarch->joypad,
- sec_joypad,
- &joypad_info, binds,
- p_rarch->keyboard_mapping_blocked,
- 0, pointer_device,
- 0, RETRO_DEVICE_ID_POINTER_Y);
- hw_state->y = ((pointer_y + 0x7fff) * (int)fb_height) / 0xFFFF;
- if (pointer_device == RARCH_DEVICE_POINTER_SCREEN)
- {
- if (hw_state->y != last_y)
- hw_state->active = true;
- last_y = hw_state->y;
- }
- else
- {
- if (pointer_y != last_y)
- hw_state->active = true;
- last_y = pointer_y;
- }
- /* Select (touch screen contact)
- * Note that releasing select also counts as activity */
- if (current_input->input_state)
- hw_state->select_pressed = (bool)current_input->input_state(
- p_rarch->current_input_data,
- p_rarch->joypad,
- sec_joypad,
- &joypad_info, binds,
- p_rarch->keyboard_mapping_blocked,
- 0, pointer_device,
- 0, RETRO_DEVICE_ID_POINTER_PRESSED);
- if (hw_state->select_pressed || (hw_state->select_pressed != last_select_pressed))
- hw_state->active = true;
- last_select_pressed = hw_state->select_pressed;
- /* Cancel (touch screen 'back' - don't know what is this, but whatever...)
- * Note that releasing cancel also counts as activity */
- if (current_input->input_state)
- hw_state->cancel_pressed = (bool)current_input->input_state(
- p_rarch->current_input_data,
- p_rarch->joypad,
- sec_joypad,
- &joypad_info, binds,
- p_rarch->keyboard_mapping_blocked,
- 0, pointer_device,
- 0, RARCH_DEVICE_ID_POINTER_BACK);
- if (hw_state->cancel_pressed || (hw_state->cancel_pressed != last_cancel_pressed))
- hw_state->active = true;
- last_cancel_pressed = hw_state->cancel_pressed;
- }
- static INLINE bool input_event_osk_show_symbol_pages(
- struct rarch_state *p_rarch)
- {
- #if defined(HAVE_LANGEXTRA)
- #if defined(HAVE_RGUI)
- menu_handle_t *menu = p_rarch->menu_driver_data;
- bool menu_has_fb = (menu &&
- menu->driver_ctx &&
- menu->driver_ctx->set_texture);
- unsigned language = *msg_hash_get_uint(MSG_HASH_USER_LANGUAGE);
- return !menu_has_fb ||
- ((language == RETRO_LANGUAGE_JAPANESE) ||
- (language == RETRO_LANGUAGE_KOREAN) ||
- (language == RETRO_LANGUAGE_CHINESE_SIMPLIFIED) ||
- (language == RETRO_LANGUAGE_CHINESE_TRADITIONAL));
- #else /* HAVE_RGUI */
- return true;
- #endif /* HAVE_RGUI */
- #else /* HAVE_LANGEXTRA */
- return false;
- #endif /* HAVE_LANGEXTRA */
- }
- static void input_event_osk_append(
- struct rarch_state *p_rarch,
- enum osk_type *osk_idx, int ptr,
- bool show_symbol_pages,
- const char *word)
- {
- #ifdef HAVE_LANGEXTRA
- if (string_is_equal(word, "\xe2\x87\xa6")) /* backspace character */
- input_keyboard_event(true, '\x7f', '\x7f', 0, RETRO_DEVICE_KEYBOARD);
- else if (string_is_equal(word, "\xe2\x8f\x8e")) /* return character */
- input_keyboard_event(true, '\n', '\n', 0, RETRO_DEVICE_KEYBOARD);
- else
- if (string_is_equal(word, "\xe2\x87\xa7")) /* up arrow */
- *osk_idx = OSK_UPPERCASE_LATIN;
- else if (string_is_equal(word, "\xe2\x87\xa9")) /* down arrow */
- *osk_idx = OSK_LOWERCASE_LATIN;
- else if (string_is_equal(word,"\xe2\x8a\x95")) /* plus sign (next button) */
- #else
- if (string_is_equal(word, "Bksp"))
- input_keyboard_event(true, '\x7f', '\x7f', 0, RETRO_DEVICE_KEYBOARD);
- else if (string_is_equal(word, "Enter"))
- input_keyboard_event(true, '\n', '\n', 0, RETRO_DEVICE_KEYBOARD);
- else
- if (string_is_equal(word, "Upper"))
- *osk_idx = OSK_UPPERCASE_LATIN;
- else if (string_is_equal(word, "Lower"))
- *osk_idx = OSK_LOWERCASE_LATIN;
- else if (string_is_equal(word, "Next"))
- #endif
- if (*osk_idx < (show_symbol_pages ? OSK_TYPE_LAST - 1 : OSK_SYMBOLS_PAGE1))
- *osk_idx = (enum osk_type)(*osk_idx + 1);
- else
- *osk_idx = ((enum osk_type)(OSK_TYPE_UNKNOWN + 1));
- else
- {
- input_keyboard_line_append(&p_rarch->keyboard_line, word);
- if (word[0] == 0)
- {
- p_rarch->osk_last_codepoint = 0;
- p_rarch->osk_last_codepoint_len = 0;
- }
- else
- osk_update_last_codepoint(
- &p_rarch->osk_last_codepoint,
- &p_rarch->osk_last_codepoint_len,
- word);
- }
- }
- static void input_event_osk_iterate(
- struct rarch_state *p_rarch,
- enum osk_type osk_idx)
- {
- switch (osk_idx)
- {
- #ifdef HAVE_LANGEXTRA
- case OSK_HIRAGANA_PAGE1:
- memcpy(p_rarch->osk_grid,
- hiragana_page1_grid,
- sizeof(hiragana_page1_grid));
- break;
- case OSK_HIRAGANA_PAGE2:
- memcpy(p_rarch->osk_grid,
- hiragana_page2_grid,
- sizeof(hiragana_page2_grid));
- break;
- case OSK_KATAKANA_PAGE1:
- memcpy(p_rarch->osk_grid,
- katakana_page1_grid,
- sizeof(katakana_page1_grid));
- break;
- case OSK_KATAKANA_PAGE2:
- memcpy(p_rarch->osk_grid,
- katakana_page2_grid,
- sizeof(katakana_page2_grid));
- break;
- #endif
- case OSK_SYMBOLS_PAGE1:
- memcpy(p_rarch->osk_grid,
- symbols_page1_grid,
- sizeof(uppercase_grid));
- break;
- case OSK_UPPERCASE_LATIN:
- memcpy(p_rarch->osk_grid,
- uppercase_grid,
- sizeof(uppercase_grid));
- break;
- case OSK_LOWERCASE_LATIN:
- default:
- memcpy(p_rarch->osk_grid,
- lowercase_grid,
- sizeof(lowercase_grid));
- break;
- }
- }
- /*
- * This function gets called in order to process all input events
- * for the current frame.
- *
- * Sends input code to menu for one frame.
- *
- * It uses as input the local variables 'input' and 'trigger_input'.
- *
- * Mouse and touch input events get processed inside this function.
- *
- * NOTE: 'input' and 'trigger_input' is sourced from the keyboard and/or
- * the gamepad. It does not contain input state derived from the mouse
- * and/or touch - this gets dealt with separately within this function.
- *
- * TODO/FIXME - maybe needs to be overhauled so we can send multiple
- * events per frame if we want to, and we shouldn't send the
- * entire button state either but do a separate event per button
- * state.
- */
- static unsigned menu_event(
- struct rarch_state *p_rarch,
- input_bits_t *p_input,
- input_bits_t *p_trigger_input,
- bool display_kb)
- {
- /* Used for key repeat */
- static float delay_timer = 0.0f;
- static float delay_count = 0.0f;
- static bool initial_held = true;
- static bool first_held = false;
- static unsigned ok_old = 0;
- unsigned ret = MENU_ACTION_NOOP;
- bool set_scroll = false;
- size_t new_scroll_accel = 0;
- struct menu_state *menu_st = &p_rarch->menu_driver_state;
- menu_input_t *menu_input = &p_rarch->menu_input_state;
- menu_input_pointer_hw_state_t *pointer_hw_state = &p_rarch->menu_input_pointer_hw_state;
- settings_t *settings = p_rarch->configuration_settings;
- bool menu_mouse_enable = settings->bools.menu_mouse_enable;
- bool menu_pointer_enable = settings->bools.menu_pointer_enable;
- bool swap_ok_cancel_btns = settings->bools.input_menu_swap_ok_cancel_buttons;
- bool menu_scroll_fast = settings->bools.menu_scroll_fast;
- unsigned menu_ok_btn = swap_ok_cancel_btns ?
- RETRO_DEVICE_ID_JOYPAD_B : RETRO_DEVICE_ID_JOYPAD_A;
- unsigned menu_cancel_btn = swap_ok_cancel_btns ?
- RETRO_DEVICE_ID_JOYPAD_A : RETRO_DEVICE_ID_JOYPAD_B;
- unsigned ok_current = BIT256_GET_PTR(p_input, menu_ok_btn);
- unsigned ok_trigger = ok_current & ~ok_old;
- unsigned i = 0;
- unsigned navigation_current = 0;
- unsigned navigation_buttons[6] =
- {
- RETRO_DEVICE_ID_JOYPAD_UP,
- RETRO_DEVICE_ID_JOYPAD_DOWN,
- RETRO_DEVICE_ID_JOYPAD_LEFT,
- RETRO_DEVICE_ID_JOYPAD_RIGHT,
- RETRO_DEVICE_ID_JOYPAD_L,
- RETRO_DEVICE_ID_JOYPAD_R
- };
- ok_old = ok_current;
- /* Accelerate only navigation buttons */
- for (i = 0; i < 6; i++)
- navigation_current |= BIT256_GET_PTR(p_input, navigation_buttons[i]);
- if (navigation_current)
- {
- if (!first_held)
- {
- /* don't run anything first frame, only capture held inputs
- * for old_input_state. */
- first_held = true;
- if (menu_scroll_fast)
- delay_timer = initial_held ? 256 : 100;
- else
- delay_timer = initial_held ? 256 : 20;
- delay_count = 0;
- }
- if (delay_count >= delay_timer)
- {
- uint32_t input_repeat = 0;
- for (i = 0; i < 6; i++)
- BIT32_SET(input_repeat, navigation_buttons[i]);
- set_scroll = true;
- first_held = false;
- p_trigger_input->data[0] |= p_input->data[0] & input_repeat;
- new_scroll_accel = menu_st->scroll.acceleration;
- if (menu_scroll_fast)
- new_scroll_accel = MIN(new_scroll_accel + 1, 64);
- else
- new_scroll_accel = MIN(new_scroll_accel + 1, 5);
- }
- initial_held = false;
- }
- else
- {
- set_scroll = true;
- first_held = false;
- initial_held = true;
- }
- if (set_scroll)
- menu_st->scroll.acceleration = (unsigned)(new_scroll_accel);
- delay_count += p_rarch->anim.delta_time;
- if (display_kb)
- {
- bool show_osk_symbols = input_event_osk_show_symbol_pages(p_rarch);
- input_event_osk_iterate(p_rarch, p_rarch->osk_idx);
- if (BIT256_GET_PTR(p_trigger_input, RETRO_DEVICE_ID_JOYPAD_DOWN))
- {
- if (p_rarch->osk_ptr < 33)
- p_rarch->osk_ptr += OSK_CHARS_PER_LINE;
- }
- if (BIT256_GET_PTR(p_trigger_input, RETRO_DEVICE_ID_JOYPAD_UP))
- {
- if (p_rarch->osk_ptr >= OSK_CHARS_PER_LINE)
- p_rarch->osk_ptr -= OSK_CHARS_PER_LINE;
- }
- if (BIT256_GET_PTR(p_trigger_input, RETRO_DEVICE_ID_JOYPAD_RIGHT))
- {
- if (p_rarch->osk_ptr < 43)
- p_rarch->osk_ptr += 1;
- }
- if (BIT256_GET_PTR(p_trigger_input, RETRO_DEVICE_ID_JOYPAD_LEFT))
- {
- if (p_rarch->osk_ptr >= 1)
- p_rarch->osk_ptr -= 1;
- }
- if (BIT256_GET_PTR(p_trigger_input, RETRO_DEVICE_ID_JOYPAD_L))
- {
- if (p_rarch->osk_idx > OSK_TYPE_UNKNOWN + 1)
- p_rarch->osk_idx = ((enum osk_type)
- (p_rarch->osk_idx - 1));
- else
- p_rarch->osk_idx = ((enum osk_type)(show_osk_symbols
- ? OSK_TYPE_LAST - 1
- : OSK_SYMBOLS_PAGE1));
- }
- if (BIT256_GET_PTR(p_trigger_input, RETRO_DEVICE_ID_JOYPAD_R))
- {
- if (p_rarch->osk_idx < (show_osk_symbols
- ? OSK_TYPE_LAST - 1
- : OSK_SYMBOLS_PAGE1))
- p_rarch->osk_idx = ((enum osk_type)(
- p_rarch->osk_idx + 1));
- else
- p_rarch->osk_idx = ((enum osk_type)(OSK_TYPE_UNKNOWN + 1));
- }
- if (BIT256_GET_PTR(p_trigger_input, menu_ok_btn))
- {
- if (p_rarch->osk_ptr >= 0)
- input_event_osk_append(
- p_rarch,
- &p_rarch->osk_idx,
- p_rarch->osk_ptr,
- show_osk_symbols,
- p_rarch->osk_grid[p_rarch->osk_ptr]);
- }
- if (BIT256_GET_PTR(p_trigger_input, menu_cancel_btn))
- input_keyboard_event(true, '\x7f', '\x7f',
- 0, RETRO_DEVICE_KEYBOARD);
- /* send return key to close keyboard input window */
- if (BIT256_GET_PTR(p_trigger_input, RETRO_DEVICE_ID_JOYPAD_START))
- input_keyboard_event(true, '\n', '\n', 0, RETRO_DEVICE_KEYBOARD);
- BIT256_CLEAR_ALL_PTR(p_trigger_input);
- }
- else
- {
- if (BIT256_GET_PTR(p_trigger_input, RETRO_DEVICE_ID_JOYPAD_UP))
- ret = MENU_ACTION_UP;
- else if (BIT256_GET_PTR(p_trigger_input, RETRO_DEVICE_ID_JOYPAD_DOWN))
- ret = MENU_ACTION_DOWN;
- else if (BIT256_GET_PTR(p_trigger_input, RETRO_DEVICE_ID_JOYPAD_LEFT))
- ret = MENU_ACTION_LEFT;
- else if (BIT256_GET_PTR(p_trigger_input, RETRO_DEVICE_ID_JOYPAD_RIGHT))
- ret = MENU_ACTION_RIGHT;
- else if (BIT256_GET_PTR(p_trigger_input, RETRO_DEVICE_ID_JOYPAD_L))
- ret = MENU_ACTION_SCROLL_UP;
- else if (BIT256_GET_PTR(p_trigger_input, RETRO_DEVICE_ID_JOYPAD_R))
- ret = MENU_ACTION_SCROLL_DOWN;
- else if (ok_trigger)
- ret = MENU_ACTION_OK;
- else if (BIT256_GET_PTR(p_trigger_input, menu_cancel_btn))
- ret = MENU_ACTION_CANCEL;
- else if (BIT256_GET_PTR(p_trigger_input, RETRO_DEVICE_ID_JOYPAD_X))
- ret = MENU_ACTION_SEARCH;
- else if (BIT256_GET_PTR(p_trigger_input, RETRO_DEVICE_ID_JOYPAD_Y))
- ret = MENU_ACTION_SCAN;
- else if (BIT256_GET_PTR(p_trigger_input, RETRO_DEVICE_ID_JOYPAD_START))
- ret = MENU_ACTION_START;
- else if (BIT256_GET_PTR(p_trigger_input, RETRO_DEVICE_ID_JOYPAD_SELECT))
- ret = MENU_ACTION_INFO;
- else if (BIT256_GET_PTR(p_trigger_input, RARCH_MENU_TOGGLE))
- ret = MENU_ACTION_TOGGLE;
- }
- /* Get pointer (mouse + touchscreen) input */
- /* > If pointer input is disabled, do nothing */
- if (!menu_mouse_enable && !menu_pointer_enable)
- menu_input->pointer.type = MENU_POINTER_DISABLED;
- else
- {
- menu_input_pointer_hw_state_t mouse_hw_state = {0};
- menu_input_pointer_hw_state_t touchscreen_hw_state = {0};
- /* Read mouse */
- if (menu_mouse_enable)
- menu_input_get_mouse_hw_state(p_rarch, &mouse_hw_state);
- /* Read touchscreen
- * Note: Could forgo this if mouse is currently active,
- * but this is 'cleaner' code... (if performance is a
- * concern - and it isn't - user can just disable touch
- * screen support) */
- if (menu_pointer_enable)
- menu_input_get_touchscreen_hw_state(
- p_rarch, &touchscreen_hw_state);
- /* Mouse takes precedence */
- if (mouse_hw_state.active)
- menu_input->pointer.type = MENU_POINTER_MOUSE;
- else if (touchscreen_hw_state.active)
- menu_input->pointer.type = MENU_POINTER_TOUCHSCREEN;
- /* Copy input from the current device */
- if (menu_input->pointer.type == MENU_POINTER_MOUSE)
- memcpy(pointer_hw_state, &mouse_hw_state, sizeof(menu_input_pointer_hw_state_t));
- else if (menu_input->pointer.type == MENU_POINTER_TOUCHSCREEN)
- memcpy(pointer_hw_state, &touchscreen_hw_state, sizeof(menu_input_pointer_hw_state_t));
- }
- /* Populate menu_input_state
- * Note: dx, dy, ptr, y_accel, etc. entries are set elsewhere */
- if (menu_input->select_inhibit || menu_input->cancel_inhibit)
- {
- menu_input->pointer.active = false;
- menu_input->pointer.pressed = false;
- menu_input->pointer.x = 0;
- menu_input->pointer.y = 0;
- }
- else
- {
- menu_input->pointer.active = pointer_hw_state->active;
- menu_input->pointer.pressed = pointer_hw_state->select_pressed;
- menu_input->pointer.x = pointer_hw_state->x;
- menu_input->pointer.y = pointer_hw_state->y;
- }
- return ret;
- }
- void menu_input_get_pointer_state(menu_input_pointer_t *pointer)
- {
- struct rarch_state *p_rarch = &rarch_st;
- menu_input_t *menu_input = &p_rarch->menu_input_state;
- if (!pointer)
- return;
- /* Copy parameters from global menu_input_state
- * (i.e. don't pass by reference)
- * This is a fast operation */
- memcpy(pointer, &menu_input->pointer, sizeof(menu_input_pointer_t));
- }
- unsigned menu_input_get_pointer_selection(void)
- {
- struct rarch_state *p_rarch = &rarch_st;
- menu_input_t *menu_input = &p_rarch->menu_input_state;
- return menu_input->ptr;
- }
- void menu_input_set_pointer_selection(unsigned selection)
- {
- struct rarch_state *p_rarch = &rarch_st;
- menu_input_t *menu_input = &p_rarch->menu_input_state;
- menu_input->ptr = selection;
- }
- void menu_input_set_pointer_y_accel(float y_accel)
- {
- struct rarch_state *p_rarch = &rarch_st;
- menu_input_t *menu_input = &p_rarch->menu_input_state;
- menu_input->pointer.y_accel = y_accel;
- }
- static void menu_input_reset(
- menu_input_t *menu_input,
- menu_input_pointer_hw_state_t *pointer_hw_state
- )
- {
- memset(menu_input, 0, sizeof(menu_input_t));
- memset(pointer_hw_state, 0, sizeof(menu_input_pointer_hw_state_t));
- }
- static float menu_input_get_dpi(struct rarch_state *p_rarch)
- {
- static unsigned last_video_width = 0;
- static unsigned last_video_height = 0;
- static float dpi = 0.0f;
- static bool dpi_cached = false;
- menu_handle_t *menu = p_rarch->menu_driver_data;
- /* Regardless of menu driver, need 'actual' screen DPI
- * Note: DPI is a fixed hardware property. To minimise performance
- * overheads we therefore only call video_context_driver_get_metrics()
- * on first run, or when the current video resolution changes */
- if (!dpi_cached ||
- (p_rarch->video_driver_width != last_video_width) ||
- (p_rarch->video_driver_height != last_video_height))
- {
- gfx_ctx_metrics_t mets;
- /* Note: If video_context_driver_get_metrics() fails,
- * we don't know what happened to dpi - so ensure it
- * is reset to a sane value */
- mets.type = DISPLAY_METRIC_DPI;
- mets.value = &dpi;
- if (!video_context_driver_get_metrics(&mets))
- dpi = 0.0f;
- dpi_cached = true;
- last_video_width = p_rarch->video_driver_width;
- last_video_height = p_rarch->video_driver_height;
- }
- /* RGUI uses a framebuffer texture, which means we
- * operate in menu space, not screen space.
- * DPI in a traditional sense is therefore meaningless,
- * so generate a substitute value based upon framebuffer
- * dimensions */
- if (dpi > 0.0f)
- {
- bool menu_has_fb =
- menu->driver_ctx
- && menu->driver_ctx->set_texture;
- if (menu_has_fb)
- {
- size_t fb_pitch;
- unsigned fb_width, fb_height;
- /* Read framebuffer info */
- gfx_display_get_fb_size(&fb_width, &fb_height, &fb_pitch);
- /* Rationale for current 'DPI' determination method:
- * - Divide screen height by DPI, to get number of vertical
- * '1 inch' squares
- * - Divide RGUI framebuffer height by number of vertical
- * '1 inch' squares to get number of menu space pixels
- * per inch
- * This is crude, but should be sufficient... */
- return ((float)fb_height / (float)p_rarch->video_driver_height) * dpi;
- }
- }
- return dpi;
- }
- static bool menu_should_pop_stack(const char *label)
- {
- /* > Info box */
- if (string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_INFO_SCREEN)))
- return true;
- /* > Help box */
- if (string_starts_with_size(label, "help", STRLEN_CONST("help")))
- if (
- string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_HELP))
- || string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_HELP_CONTROLS))
- || string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_HELP_WHAT_IS_A_CORE))
- || string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_HELP_LOADING_CONTENT))
- || string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_HELP_SCANNING_CONTENT))
- || string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_HELP_CHANGE_VIRTUAL_GAMEPAD))
- || string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_HELP_AUDIO_VIDEO_TROUBLESHOOTING))
- || string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_HELP_SEND_DEBUG_INFO))
- || string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_CHEEVOS_DESCRIPTION)))
- return true;
- if (
- string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_CHEEVOS_DESCRIPTION)))
- return true;
- return false;
- }
- /* Used to close an active message box (help or info)
- * TODO/FIXME: The way that message boxes are handled
- * is complete garbage. generic_menu_iterate() and
- * message boxes in general need a total rewrite.
- * I consider this current 'close_messagebox' a hack,
- * but at least it prevents undefined/dangerous
- * behaviour... */
- static void menu_input_pointer_close_messagebox(struct menu_state *menu_st)
- {
- const char *label = NULL;
- /* Determine whether this is a help or info
- * message box */
- file_list_get_last(MENU_LIST_GET(menu_st->entries.list, 0),
- NULL, &label, NULL, NULL);
- /* Pop stack, if required */
- if (menu_should_pop_stack(label))
- {
- size_t selection = menu_st->selection_ptr;
- size_t new_selection = selection;
- menu_entries_pop_stack(&new_selection, 0, 0);
- menu_st->selection_ptr = selection;
- }
- }
- static int menu_input_pointer_post_iterate(
- struct rarch_state *p_rarch,
- retro_time_t current_time,
- menu_file_list_cbs_t *cbs,
- menu_entry_t *entry, unsigned action)
- {
- static retro_time_t start_time = 0;
- static int16_t start_x = 0;
- static int16_t start_y = 0;
- static int16_t last_x = 0;
- static int16_t last_y = 0;
- static uint16_t dx_start_right_max = 0;
- static uint16_t dx_start_left_max = 0;
- static uint16_t dy_start_up_max = 0;
- static uint16_t dy_start_down_max = 0;
- static bool last_select_pressed = false;
- static bool last_cancel_pressed = false;
- static bool last_left_pressed = false;
- static bool last_right_pressed = false;
- static retro_time_t last_left_action_time = 0;
- static retro_time_t last_right_action_time = 0;
- static retro_time_t last_press_direction_time = 0;
- bool attenuate_y_accel = true;
- bool osk_active = menu_input_dialog_get_display_kb();
- bool messagebox_active = false;
- int ret = 0;
- menu_input_pointer_hw_state_t *pointer_hw_state = &p_rarch->menu_input_pointer_hw_state;
- menu_input_t *menu_input = &p_rarch->menu_input_state;
- menu_handle_t *menu = p_rarch->menu_driver_data;
- struct menu_state *menu_st = &p_rarch->menu_driver_state;
- /* Check whether a message box is currently
- * being shown
- * > Note: This ignores input bind dialogs,
- * since input binding overrides normal input
- * and must be handled separately... */
- if (menu)
- messagebox_active = BIT64_GET(
- menu->state, MENU_STATE_RENDER_MESSAGEBOX) &&
- !string_is_empty(menu->menu_state_msg);
- /* If onscreen keyboard is shown and we currently have
- * active mouse input, highlight key under mouse cursor */
- if (osk_active &&
- (menu_input->pointer.type == MENU_POINTER_MOUSE) &&
- pointer_hw_state->active)
- {
- menu_ctx_pointer_t point;
- point.x = pointer_hw_state->x;
- point.y = pointer_hw_state->y;
- point.ptr = 0;
- point.cbs = NULL;
- point.entry = NULL;
- point.action = 0;
- point.gesture = MENU_INPUT_GESTURE_NONE;
- point.retcode = 0;
- menu_driver_ctl(RARCH_MENU_CTL_OSK_PTR_AT_POS, &point);
- if (point.retcode > -1)
- p_rarch->osk_ptr = point.retcode;
- }
- /* Select + X/Y position */
- if (!menu_input->select_inhibit)
- {
- if (pointer_hw_state->select_pressed)
- {
- int16_t x = pointer_hw_state->x;
- int16_t y = pointer_hw_state->y;
- static float accel0 = 0.0f;
- static float accel1 = 0.0f;
- /* Transition from select unpressed to select pressed */
- if (!last_select_pressed)
- {
- menu_ctx_pointer_t point;
- /* Initialise variables */
- start_time = current_time;
- start_x = x;
- start_y = y;
- last_x = x;
- last_y = y;
- dx_start_right_max = 0;
- dx_start_left_max = 0;
- dy_start_up_max = 0;
- dy_start_down_max = 0;
- accel0 = 0.0f;
- accel1 = 0.0f;
- last_press_direction_time = 0;
- /* If we are not currently showing the onscreen
- * keyboard or a message box, trigger a 'pointer
- * down' event */
- if (!osk_active && !messagebox_active)
- {
- point.x = x;
- point.y = y;
- /* Note: menu_input->ptr is meaningless here when
- * using a touchscreen... */
- point.ptr = menu_input->ptr;
- point.cbs = cbs;
- point.entry = entry;
- point.action = action;
- point.gesture = MENU_INPUT_GESTURE_NONE;
- menu_driver_ctl(RARCH_MENU_CTL_POINTER_DOWN, &point);
- ret = point.retcode;
- }
- }
- else
- {
- /* Pointer is being held down
- * (i.e. for more than one frame) */
- float dpi = menu ? menu_input_get_dpi(p_rarch) : 0.0f;
- /* > Update deltas + acceleration & detect press direction
- * Note: We only do this if the pointer has moved above
- * a certain threshold - this requires dpi info */
- if (dpi > 0.0f)
- {
- uint16_t dpi_threshold_drag =
- (uint16_t)((dpi * MENU_INPUT_DPI_THRESHOLD_DRAG) + 0.5f);
- int16_t dx_start = x - start_x;
- int16_t dy_start = y - start_y;
- uint16_t dx_start_abs = dx_start < 0 ? dx_start * -1 : dx_start;
- uint16_t dy_start_abs = dy_start < 0 ? dy_start * -1 : dy_start;
- if ((dx_start_abs > dpi_threshold_drag) ||
- (dy_start_abs > dpi_threshold_drag))
- {
- uint16_t dpi_threshold_press_direction_min =
- (uint16_t)((dpi * MENU_INPUT_DPI_THRESHOLD_PRESS_DIRECTION_MIN) + 0.5f);
- uint16_t dpi_threshold_press_direction_max =
- (uint16_t)((dpi * MENU_INPUT_DPI_THRESHOLD_PRESS_DIRECTION_MAX) + 0.5f);
- uint16_t dpi_threshold_press_direction_tangent =
- (uint16_t)((dpi * MENU_INPUT_DPI_THRESHOLD_PRESS_DIRECTION_TANGENT) + 0.5f);
- enum menu_input_pointer_press_direction
- press_direction = MENU_INPUT_PRESS_DIRECTION_NONE;
- float press_direction_amplitude = 0.0f;
- retro_time_t press_direction_delay = MENU_INPUT_PRESS_DIRECTION_DELAY_MAX;
- /* Pointer has moved a sufficient distance to
- * trigger a 'dragged' state */
- menu_input->pointer.dragged = true;
- /* Here we diverge:
- * > If onscreen keyboard or a message box is
- * active, pointer deltas, acceleration and
- * press direction must be inhibited
- * > If not, input is processed normally */
- if (osk_active || messagebox_active)
- {
- /* Inhibit normal pointer input */
- menu_input->pointer.dx = 0;
- menu_input->pointer.dy = 0;
- menu_input->pointer.y_accel = 0.0f;
- menu_input->pointer.press_direction = MENU_INPUT_PRESS_DIRECTION_NONE;
- accel0 = 0.0f;
- accel1 = 0.0f;
- attenuate_y_accel = false;
- }
- else
- {
- /* Assign current deltas */
- menu_input->pointer.dx = x - last_x;
- menu_input->pointer.dy = y - last_y;
- /* Update maximum start->current deltas */
- if (dx_start > 0)
- dx_start_right_max = (dx_start_abs > dx_start_right_max) ?
- dx_start_abs : dx_start_right_max;
- else
- dx_start_left_max = (dx_start_abs > dx_start_left_max) ?
- dx_start_abs : dx_start_left_max;
- if (dy_start > 0)
- dy_start_down_max = (dy_start_abs > dy_start_down_max) ?
- dy_start_abs : dy_start_down_max;
- else
- dy_start_up_max = (dy_start_abs > dy_start_up_max) ?
- dy_start_abs : dy_start_up_max;
- /* Magic numbers... */
- menu_input->pointer.y_accel = (accel0 + accel1 + (float)menu_input->pointer.dy) / 3.0f;
- accel0 = accel1;
- accel1 = menu_input->pointer.y_accel;
- /* Acceleration decays over time - but if the value
- * has been set on this frame, attenuation should
- * be skipped */
- attenuate_y_accel = false;
- /* Check if pointer is being held in a particular
- * direction */
- menu_input->pointer.press_direction = MENU_INPUT_PRESS_DIRECTION_NONE;
- /* > Press directions are actually triggered as a pulse train,
- * since a continuous direction prevents fine control in the
- * context of menu actions (i.e. it would be the same
- * as always holding down a cursor key all the time - too fast
- * to control). We therefore apply a low pass filter, with
- * a variable frequency based upon the distance the user has
- * dragged the pointer */
- /* > Horizontal */
- if ((dx_start_abs >= dpi_threshold_press_direction_min) &&
- (dy_start_abs < dpi_threshold_press_direction_tangent))
- {
- press_direction = (dx_start > 0) ?
- MENU_INPUT_PRESS_DIRECTION_RIGHT : MENU_INPUT_PRESS_DIRECTION_LEFT;
- /* Get effective amplitude of press direction offset */
- press_direction_amplitude =
- (float)(dx_start_abs - dpi_threshold_press_direction_min) /
- (float)(dpi_threshold_press_direction_max - dpi_threshold_press_direction_min);
- }
- /* > Vertical */
- else if ((dy_start_abs >= dpi_threshold_press_direction_min) &&
- (dx_start_abs < dpi_threshold_press_direction_tangent))
- {
- press_direction = (dy_start > 0) ?
- MENU_INPUT_PRESS_DIRECTION_DOWN : MENU_INPUT_PRESS_DIRECTION_UP;
- /* Get effective amplitude of press direction offset */
- press_direction_amplitude =
- (float)(dy_start_abs - dpi_threshold_press_direction_min) /
- (float)(dpi_threshold_press_direction_max - dpi_threshold_press_direction_min);
- }
- if (press_direction != MENU_INPUT_PRESS_DIRECTION_NONE)
- {
- /* > Update low pass filter frequency */
- if (press_direction_amplitude > 1.0f)
- press_direction_delay = MENU_INPUT_PRESS_DIRECTION_DELAY_MIN;
- else
- press_direction_delay = MENU_INPUT_PRESS_DIRECTION_DELAY_MIN +
- ((MENU_INPUT_PRESS_DIRECTION_DELAY_MAX - MENU_INPUT_PRESS_DIRECTION_DELAY_MIN)*
- (1.0f - press_direction_amplitude));
- /* > Apply low pass filter */
- if (current_time - last_press_direction_time > press_direction_delay)
- {
- menu_input->pointer.press_direction = press_direction;
- last_press_direction_time = current_time;
- }
- }
- }
- }
- else
- {
- /* Pointer is stationary */
- menu_input->pointer.dx = 0;
- menu_input->pointer.dy = 0;
- menu_input->pointer.press_direction = MENU_INPUT_PRESS_DIRECTION_NONE;
- /* Standard behaviour (on Android, at least) is to stop
- * scrolling when the user touches the screen. We should
- * therefore 'reset' y acceleration whenever the pointer
- * is stationary - with two caveats:
- * - We only disable scrolling if the pointer *remains*
- * stationary. If the pointer is held down then
- * subsequently moves, normal scrolling should resume
- * - Halting the scroll immediately produces a very
- * unpleasant 'jerky' user experience. To avoid this,
- * we add a small delay between detecting a pointer
- * down event and forcing y acceleration to zero
- * NOTE: Of course, we must also 'reset' y acceleration
- * whenever the onscreen keyboard or a message box is
- * shown */
- if ((!menu_input->pointer.dragged &&
- (menu_input->pointer.press_duration > MENU_INPUT_Y_ACCEL_RESET_DELAY)) ||
- (osk_active || messagebox_active))
- {
- menu_input->pointer.y_accel = 0.0f;
- accel0 = 0.0f;
- accel1 = 0.0f;
- attenuate_y_accel = false;
- }
- }
- }
- else
- {
- /* No dpi info - just fallback to zero... */
- menu_input->pointer.dx = 0;
- menu_input->pointer.dy = 0;
- menu_input->pointer.y_accel = 0.0f;
- menu_input->pointer.press_direction = MENU_INPUT_PRESS_DIRECTION_NONE;
- accel0 = 0.0f;
- accel1 = 0.0f;
- attenuate_y_accel = false;
- }
- /* > Update remaining variables */
- menu_input->pointer.press_duration = current_time - start_time;
- last_x = x;
- last_y = y;
- }
- }
- else if (last_select_pressed)
- {
- /* Transition from select pressed to select unpressed */
- int16_t x;
- int16_t y;
- menu_ctx_pointer_t point;
- if (menu_input->pointer.dragged)
- {
- /* Pointer has moved.
- * When using a touchscreen, releasing a press
- * resets the x/y position - so cannot use
- * current hardware x/y values. Instead, use
- * previous position from last time that a
- * press was active */
- x = last_x;
- y = last_y;
- }
- else
- {
- /* Pointer is considered stationary,
- * so use start position */
- x = start_x;
- y = start_y;
- }
- point.x = x;
- point.y = y;
- point.ptr = menu_input->ptr;
- point.cbs = cbs;
- point.entry = entry;
- point.action = action;
- point.gesture = MENU_INPUT_GESTURE_NONE;
- /* On screen keyboard overrides normal menu input... */
- if (osk_active)
- {
- /* If pointer has been 'dragged', then it counts as
- * a miss. Only register 'release' event if pointer
- * has remained stationary */
- if (!menu_input->pointer.dragged)
- {
- menu_driver_ctl(RARCH_MENU_CTL_OSK_PTR_AT_POS, &point);
- if (point.retcode > -1)
- {
- bool show_osk_symbols = input_event_osk_show_symbol_pages(p_rarch);
- p_rarch->osk_ptr = point.retcode;
- input_event_osk_append(
- p_rarch,
- &p_rarch->osk_idx,
- point.retcode,
- show_osk_symbols,
- p_rarch->osk_grid[p_rarch->osk_ptr]);
- }
- }
- }
- /* Message boxes override normal menu input...
- * > If a message box is shown, any kind of pointer
- * gesture should close it */
- else if (messagebox_active)
- menu_input_pointer_close_messagebox(
- &p_rarch->menu_driver_state);
- /* Normal menu input */
- else
- {
- /* Detect gesture type */
- if (!menu_input->pointer.dragged)
- {
- /* Pointer hasn't moved - check press duration */
- if (menu_input->pointer.press_duration
- < MENU_INPUT_PRESS_TIME_SHORT)
- point.gesture = MENU_INPUT_GESTURE_TAP;
- else if (menu_input->pointer.press_duration
- < MENU_INPUT_PRESS_TIME_LONG)
- point.gesture = MENU_INPUT_GESTURE_SHORT_PRESS;
- else
- point.gesture = MENU_INPUT_GESTURE_LONG_PRESS;
- }
- else
- {
- /* Pointer has moved - check if this is a swipe */
- float dpi = menu ? menu_input_get_dpi(p_rarch) : 0.0f;
- if ((dpi > 0.0f)
- &&
- (menu_input->pointer.press_duration <
- MENU_INPUT_SWIPE_TIMEOUT))
- {
- uint16_t dpi_threshold_swipe =
- (uint16_t)((dpi * MENU_INPUT_DPI_THRESHOLD_SWIPE) + 0.5f);
- uint16_t dpi_threshold_swipe_tangent =
- (uint16_t)((dpi * MENU_INPUT_DPI_THRESHOLD_SWIPE_TANGENT) + 0.5f);
- int16_t dx_start = x - start_x;
- int16_t dy_start = y - start_y;
- uint16_t dx_start_right_final = 0;
- uint16_t dx_start_left_final = 0;
- uint16_t dy_start_up_final = 0;
- uint16_t dy_start_down_final = 0;
- /* Get final deltas */
- if (dx_start > 0)
- dx_start_right_final = (uint16_t)dx_start;
- else
- dx_start_left_final = (uint16_t)
- (dx_start * -1);
- if (dy_start > 0)
- dy_start_down_final = (uint16_t)dy_start;
- else
- dy_start_up_final = (uint16_t)
- (dy_start * -1);
- /* Swipe right */
- if ( (dx_start_right_final > dpi_threshold_swipe)
- && (dx_start_left_max < dpi_threshold_swipe_tangent)
- && (dy_start_up_max < dpi_threshold_swipe_tangent)
- && (dy_start_down_max < dpi_threshold_swipe_tangent)
- )
- point.gesture = MENU_INPUT_GESTURE_SWIPE_RIGHT;
- /* Swipe left */
- else if (
- (dx_start_right_max < dpi_threshold_swipe_tangent)
- && (dx_start_left_final > dpi_threshold_swipe)
- && (dy_start_up_max < dpi_threshold_swipe_tangent)
- && (dy_start_down_max < dpi_threshold_swipe_tangent)
- )
- point.gesture = MENU_INPUT_GESTURE_SWIPE_LEFT;
- /* Swipe up */
- else if (
- (dx_start_right_max < dpi_threshold_swipe_tangent)
- && (dx_start_left_max < dpi_threshold_swipe_tangent)
- && (dy_start_up_final > dpi_threshold_swipe)
- && (dy_start_down_max < dpi_threshold_swipe_tangent)
- )
- point.gesture = MENU_INPUT_GESTURE_SWIPE_UP;
- /* Swipe down */
- else if (
- (dx_start_right_max < dpi_threshold_swipe_tangent)
- && (dx_start_left_max < dpi_threshold_swipe_tangent)
- && (dy_start_up_max < dpi_threshold_swipe_tangent)
- && (dy_start_down_final > dpi_threshold_swipe)
- )
- point.gesture = MENU_INPUT_GESTURE_SWIPE_DOWN;
- }
- }
- /* Trigger a 'pointer up' event */
- menu_driver_ctl(RARCH_MENU_CTL_POINTER_UP, &point);
- ret = point.retcode;
- }
- /* Reset variables */
- start_x = 0;
- start_y = 0;
- last_x = 0;
- last_y = 0;
- dx_start_right_max = 0;
- dx_start_left_max = 0;
- dy_start_up_max = 0;
- dy_start_down_max = 0;
- last_press_direction_time = 0;
- menu_input->pointer.press_duration = 0;
- menu_input->pointer.press_direction = MENU_INPUT_PRESS_DIRECTION_NONE;
- menu_input->pointer.dx = 0;
- menu_input->pointer.dy = 0;
- menu_input->pointer.dragged = false;
- }
- }
- /* Adjust acceleration
- * > If acceleration has not been set on this frame,
- * apply normal attenuation */
- if (attenuate_y_accel)
- menu_input->pointer.y_accel *= MENU_INPUT_Y_ACCEL_DECAY_FACTOR;
- /* If select has been released, disable any existing
- * select inhibit */
- if (!pointer_hw_state->select_pressed)
- menu_input->select_inhibit = false;
- /* Cancel */
- if ( !menu_input->cancel_inhibit
- && pointer_hw_state->cancel_pressed
- && !last_cancel_pressed)
- {
- /* If currently showing a message box, close it */
- if (messagebox_active)
- menu_input_pointer_close_messagebox(&p_rarch->menu_driver_state);
- /* ...otherwise, invoke standard MENU_ACTION_CANCEL
- * action */
- else
- {
- size_t selection = menu_st->selection_ptr;
- ret = menu_entry_action(entry, selection, MENU_ACTION_CANCEL);
- }
- }
- /* If cancel has been released, disable any existing
- * cancel inhibit */
- if (!pointer_hw_state->cancel_pressed)
- menu_input->cancel_inhibit = false;
- if (!messagebox_active)
- {
- /* Up/Down
- * > Note 1: These always correspond to a mouse wheel, which
- * handles differently from other inputs - i.e. we don't
- * want a 'last pressed' check
- * > Note 2: If a message box is currently shown, must
- * inhibit input */
- /* > Up */
- if (pointer_hw_state->up_pressed)
- {
- size_t selection = menu_st->selection_ptr;
- ret = menu_entry_action(
- entry, selection, MENU_ACTION_UP);
- }
- /* > Down */
- if (pointer_hw_state->down_pressed)
- {
- size_t selection = menu_st->selection_ptr;
- ret = menu_entry_action(
- entry, selection, MENU_ACTION_DOWN);
- }
- /* Left/Right
- * > Note 1: These also always correspond to a mouse wheel...
- * In this case, it's a mouse wheel *tilt* operation, which
- * is incredibly annoying because holding a tilt direction
- * rapidly toggles the input state. The repeat speed is so
- * high that any sort of useable control is impossible - so
- * we have to apply a 'low pass' filter by ignoring inputs
- * that occur below a certain frequency...
- * > Note 2: If a message box is currently shown, must
- * inhibit input */
- /* > Left */
- if ( pointer_hw_state->left_pressed
- && !last_left_pressed)
- {
- if (current_time - last_left_action_time
- > MENU_INPUT_HORIZ_WHEEL_DELAY)
- {
- size_t selection = menu_st->selection_ptr;
- last_left_action_time = current_time;
- ret = menu_entry_action(
- entry, selection, MENU_ACTION_LEFT);
- }
- }
- /* > Right */
- if (
- pointer_hw_state->right_pressed
- && !last_right_pressed)
- {
- if (current_time - last_right_action_time
- > MENU_INPUT_HORIZ_WHEEL_DELAY)
- {
- size_t selection = menu_st->selection_ptr;
- last_right_action_time = current_time;
- ret = menu_entry_action(
- entry, selection, MENU_ACTION_RIGHT);
- }
- }
- }
- last_select_pressed = pointer_hw_state->select_pressed;
- last_cancel_pressed = pointer_hw_state->cancel_pressed;
- last_left_pressed = pointer_hw_state->left_pressed;
- last_right_pressed = pointer_hw_state->right_pressed;
- return ret;
- }
- static int menu_input_post_iterate(
- struct rarch_state *p_rarch,
- unsigned action,
- retro_time_t current_time)
- {
- menu_entry_t entry;
- struct menu_state *menu_st = &p_rarch->menu_driver_state;
- menu_list_t *menu_list = menu_st->entries.list;
- file_list_t *selection_buf = menu_list ? MENU_LIST_GET_SELECTION(menu_list, (unsigned)0) : NULL;
- size_t selection = menu_st->selection_ptr;
- menu_file_list_cbs_t *cbs = selection_buf ?
- (menu_file_list_cbs_t*)selection_buf->list[selection].actiondata
- : NULL;
- MENU_ENTRY_INIT(entry);
- /* Note: If menu_input_pointer_post_iterate() is
- * modified, will have to verify that these
- * parameters remain unused... */
- entry.rich_label_enabled = false;
- entry.value_enabled = false;
- entry.sublabel_enabled = false;
- menu_entry_get(&entry, 0, selection, NULL, false);
- return menu_input_pointer_post_iterate(p_rarch,
- current_time, cbs, &entry, action);
- }
- #endif
- static INLINE bool input_keys_pressed_other_sources(
- struct rarch_state *p_rarch,
- unsigned i,
- input_bits_t* p_new_state)
- {
- #ifdef HAVE_OVERLAY
- if (p_rarch->overlay_ptr &&
- ((BIT256_GET(p_rarch->overlay_ptr->overlay_state.buttons, i))))
- return true;
- #endif
- #ifdef HAVE_COMMAND
- if (p_rarch->input_driver_command)
- return ((i < RARCH_BIND_LIST_END)
- && p_rarch->input_driver_command->state[i]);
- #endif
- #ifdef HAVE_NETWORKGAMEPAD
- /* Only process key presses related to game input if using Remote RetroPad */
- if (i < RARCH_CUSTOM_BIND_LIST_END
- && p_rarch->input_driver_remote
- && INPUT_REMOTE_KEY_PRESSED(p_rarch, i, 0))
- return true;
- #endif
- return false;
- }
- /**
- * input_keys_pressed:
- *
- * Grab an input sample for this frame.
- *
- * Returns: Input sample containing a mask of all pressed keys.
- */
- static void input_keys_pressed(
- unsigned port,
- bool is_menu,
- int input_hotkey_block_delay,
- struct rarch_state *p_rarch,
- input_bits_t *p_new_state,
- const struct retro_keybind **binds,
- const struct retro_keybind *binds_norm,
- const struct retro_keybind *binds_auto,
- rarch_joypad_info_t *joypad_info)
- {
- unsigned i;
- #ifdef HAVE_MFI
- const input_device_driver_t
- *sec_joypad = p_rarch->sec_joypad;
- #else
- const input_device_driver_t
- *sec_joypad = NULL;
- #endif
- if (CHECK_INPUT_DRIVER_BLOCK_HOTKEY(binds_norm, binds_auto))
- {
- if ( input_state_wrap(
- p_rarch->current_input,
- p_rarch->current_input_data,
- p_rarch->joypad,
- sec_joypad,
- joypad_info,
- &binds[port],
- p_rarch->keyboard_mapping_blocked,
- port, RETRO_DEVICE_JOYPAD, 0,
- RARCH_ENABLE_HOTKEY))
- {
- if (p_rarch->input_hotkey_block_counter < input_hotkey_block_delay)
- p_rarch->input_hotkey_block_counter++;
- else
- p_rarch->input_driver_block_libretro_input = true;
- }
- else
- {
- p_rarch->input_hotkey_block_counter = 0;
- p_rarch->input_driver_block_hotkey = true;
- }
- }
- if ( !is_menu
- && binds[port][RARCH_GAME_FOCUS_TOGGLE].valid)
- {
- const struct retro_keybind *focus_binds_auto =
- &input_autoconf_binds[port][RARCH_GAME_FOCUS_TOGGLE];
- const struct retro_keybind *focus_normal =
- &binds[port][RARCH_GAME_FOCUS_TOGGLE];
- /* Allows rarch_focus_toggle hotkey to still work
- * even though every hotkey is blocked */
- if (CHECK_INPUT_DRIVER_BLOCK_HOTKEY(
- focus_normal, focus_binds_auto))
- {
- if (input_state_wrap(
- p_rarch->current_input,
- p_rarch->current_input_data,
- p_rarch->joypad,
- sec_joypad,
- joypad_info,
- &binds[port],
- p_rarch->keyboard_mapping_blocked,
- port,
- RETRO_DEVICE_JOYPAD, 0, RARCH_GAME_FOCUS_TOGGLE))
- p_rarch->input_driver_block_hotkey = false;
- }
- }
- /* Check the libretro input first */
- if (p_rarch->input_driver_block_libretro_input)
- {
- for (i = 0; i < RARCH_FIRST_META_KEY; i++)
- {
- if (input_keys_pressed_other_sources(p_rarch,
- i, p_new_state))
- {
- BIT256_SET_PTR(p_new_state, i);
- }
- }
- }
- else
- {
- int16_t ret = input_state_wrap(
- p_rarch->current_input,
- p_rarch->current_input_data,
- p_rarch->joypad,
- sec_joypad,
- joypad_info, &binds[port],
- p_rarch->keyboard_mapping_blocked,
- port, RETRO_DEVICE_JOYPAD, 0,
- RETRO_DEVICE_ID_JOYPAD_MASK);
- for (i = 0; i < RARCH_FIRST_META_KEY; i++)
- {
- if (
- (ret & (UINT64_C(1) << i)) ||
- input_keys_pressed_other_sources(p_rarch,
- i, p_new_state))
- {
- BIT256_SET_PTR(p_new_state, i);
- }
- }
- }
- /* Check the hotkeys */
- if (p_rarch->input_driver_block_hotkey)
- {
- for (i = RARCH_FIRST_META_KEY; i < RARCH_BIND_LIST_END; i++)
- {
- if (
- BIT64_GET(lifecycle_state, i)
- || input_keys_pressed_other_sources(p_rarch, i, p_new_state))
- {
- BIT256_SET_PTR(p_new_state, i);
- }
- }
- }
- else
- {
- for (i = RARCH_FIRST_META_KEY; i < RARCH_BIND_LIST_END; i++)
- {
- bool bit_pressed = binds[port][i].valid
- && input_state_wrap(
- p_rarch->current_input,
- p_rarch->current_input_data,
- p_rarch->joypad,
- sec_joypad,
- joypad_info,
- &binds[port],
- p_rarch->keyboard_mapping_blocked,
- port, RETRO_DEVICE_JOYPAD, 0, i);
- if ( bit_pressed
- || BIT64_GET(lifecycle_state, i)
- || input_keys_pressed_other_sources(p_rarch, i, p_new_state))
- {
- BIT256_SET_PTR(p_new_state, i);
- }
- }
- }
- }
- void *input_driver_get_data(void)
- {
- struct rarch_state *p_rarch = &rarch_st;
- return p_rarch->current_input_data;
- }
- void input_driver_init_joypads(void)
- {
- struct rarch_state *p_rarch = &rarch_st;
- settings_t *settings = p_rarch->configuration_settings;
- p_rarch->joypad = input_joypad_init_driver(
- settings->arrays.input_joypad_driver,
- p_rarch->current_input_data);
- #ifdef HAVE_MFI
- p_rarch->sec_joypad = input_joypad_init_driver(
- "mfi",
- p_rarch->current_input_data);
- #endif
- }
- void *input_driver_init_wrap(input_driver_t *input, const char *name)
- {
- void *ret = NULL;
- if (!input)
- return NULL;
- if ((ret = input->init(name)))
- {
- input_driver_init_joypads();
- return ret;
- }
- return NULL;
- }
- static bool input_driver_init(struct rarch_state *p_rarch)
- {
- if (p_rarch->current_input)
- {
- settings_t *settings = p_rarch->configuration_settings;
- p_rarch->current_input_data = input_driver_init_wrap(
- p_rarch->current_input, settings->arrays.input_joypad_driver);
- }
- return (p_rarch->current_input_data != NULL);
- }
- static bool input_driver_find_driver(struct rarch_state *p_rarch)
- {
- settings_t *settings = p_rarch->configuration_settings;
- int i = (int)driver_find_index(
- "input_driver",
- settings->arrays.input_driver);
- if (i >= 0)
- {
- p_rarch->current_input = (input_driver_t*)input_drivers[i];
- RARCH_LOG("[Input]: Found input driver: \"%s\".\n",
- p_rarch->current_input->ident);
- }
- else
- {
- unsigned d;
- RARCH_ERR("Couldn't find any input driver named \"%s\"\n",
- settings->arrays.input_driver);
- RARCH_LOG_OUTPUT("Available input drivers are:\n");
- for (d = 0; input_drivers[d]; d++)
- RARCH_LOG_OUTPUT("\t%s\n", input_drivers[d]->ident);
- RARCH_WARN("Going to default to first input driver...\n");
- p_rarch->current_input = (input_driver_t*)input_drivers[0];
- if (!p_rarch->current_input)
- {
- retroarch_fail(1, "find_input_driver()");
- return false;
- }
- }
- return true;
- }
- void input_driver_set_nonblock_state(void)
- {
- struct rarch_state *p_rarch = &rarch_st;
- p_rarch->input_driver_nonblock_state = true;
- }
- void input_driver_unset_nonblock_state(void)
- {
- struct rarch_state *p_rarch = &rarch_st;
- p_rarch->input_driver_nonblock_state = false;
- }
- #ifdef HAVE_COMMAND
- static bool input_driver_init_command(struct rarch_state *p_rarch)
- {
- settings_t *settings = p_rarch->configuration_settings;
- bool input_stdin_cmd_enable = settings->bools.stdin_cmd_enable;
- bool input_network_cmd_enable = settings->bools.network_cmd_enable;
- unsigned network_cmd_port = settings->uints.network_cmd_port;
- bool grab_stdin = p_rarch->current_input->grab_stdin &&
- p_rarch->current_input->grab_stdin(p_rarch->current_input_data);
- if (!input_stdin_cmd_enable && !input_network_cmd_enable)
- return false;
- if (input_stdin_cmd_enable && grab_stdin)
- {
- RARCH_WARN("stdin command interface is desired, but input driver has already claimed stdin.\n"
- "Cannot use this command interface.\n");
- }
- p_rarch->input_driver_command = (command_t*)
- calloc(1, sizeof(*p_rarch->input_driver_command));
- if (p_rarch->input_driver_command)
- if (command_network_new(
- p_rarch->input_driver_command,
- input_stdin_cmd_enable && !grab_stdin,
- input_network_cmd_enable,
- network_cmd_port))
- return true;
- RARCH_ERR("Failed to initialize command interface.\n");
- return false;
- }
- static void input_driver_deinit_command(struct rarch_state *p_rarch)
- {
- if (p_rarch->input_driver_command)
- command_free(p_rarch->input_driver_command);
- p_rarch->input_driver_command = NULL;
- }
- #endif
- #ifdef HAVE_NETWORKGAMEPAD
- static input_remote_t *input_driver_init_remote(
- settings_t *settings,
- unsigned num_active_users)
- {
- unsigned network_remote_base_port = settings->uints.network_remote_base_port;
- return input_remote_new(
- settings,
- network_remote_base_port,
- num_active_users);
- }
- #endif
- float *input_driver_get_float(enum input_action action)
- {
- struct rarch_state *p_rarch = &rarch_st;
- switch (action)
- {
- case INPUT_ACTION_AXIS_THRESHOLD:
- return &p_rarch->input_driver_axis_threshold;
- default:
- case INPUT_ACTION_NONE:
- break;
- }
- return NULL;
- }
- unsigned *input_driver_get_uint(enum input_action action)
- {
- struct rarch_state *p_rarch = &rarch_st;
- switch (action)
- {
- case INPUT_ACTION_MAX_USERS:
- return &p_rarch->input_driver_max_users;
- default:
- case INPUT_ACTION_NONE:
- break;
- }
- return NULL;
- }
- /**
- * config_get_joypad_driver_options:
- *
- * Get an enumerated list of all joypad driver names, separated by '|'.
- *
- * Returns: string listing of all joypad driver names, separated by '|'.
- **/
- const char* config_get_joypad_driver_options(void)
- {
- return char_list_new_special(STRING_LIST_INPUT_JOYPAD_DRIVERS, NULL);
- }
- /**
- * input_joypad_init_first:
- *
- * Finds first suitable joypad driver and initializes.
- *
- * Returns: joypad driver if found, otherwise NULL.
- **/
- static const input_device_driver_t *input_joypad_init_first(void *data)
- {
- unsigned i;
- for (i = 0; joypad_drivers[i]; i++)
- {
- if ( joypad_drivers[i]
- && joypad_drivers[i]->init)
- {
- void *ptr = joypad_drivers[i]->init(data);
- if (ptr)
- {
- RARCH_LOG("[Joypad]: Found joypad driver: \"%s\".\n",
- joypad_drivers[i]->ident);
- return joypad_drivers[i];
- }
- }
- }
- return NULL;
- }
- /**
- * input_joypad_init_driver:
- * @ident : identifier of driver to initialize.
- *
- * Initialize a joypad driver of name @ident.
- *
- * If ident points to NULL or a zero-length string,
- * equivalent to calling input_joypad_init_first().
- *
- * Returns: joypad driver if found, otherwise NULL.
- **/
- const input_device_driver_t *input_joypad_init_driver(
- const char *ident, void *data)
- {
- unsigned i;
- if (ident && *ident)
- {
- for (i = 0; joypad_drivers[i]; i++)
- {
- if (string_is_equal(ident, joypad_drivers[i]->ident)
- && joypad_drivers[i]->init)
- {
- void *ptr = joypad_drivers[i]->init(data);
- if (ptr)
- {
- RARCH_LOG("[Joypad]: Found joypad driver: \"%s\".\n",
- joypad_drivers[i]->ident);
- return joypad_drivers[i];
- }
- }
- }
- }
- return input_joypad_init_first(data);
- }
- bool input_key_pressed(int key, bool keyboard_pressed)
- {
- /* If a keyboard key is pressed then immediately return
- * true, otherwise call button_is_pressed to determine
- * if the input comes from another input device */
- if (!(
- (key < RARCH_BIND_LIST_END)
- && keyboard_pressed
- )
- )
- {
- rarch_joypad_info_t joypad_info;
- struct rarch_state
- *p_rarch = &rarch_st;
- const input_device_driver_t
- *joypad = (const input_device_driver_t*)p_rarch->joypad;
- joypad_info.joy_idx = 0;
- joypad_info.auto_binds = input_autoconf_binds[0];
- joypad_info.axis_threshold = p_rarch->input_driver_axis_threshold;
- return button_is_pressed(
- joypad, &joypad_info,
- input_config_binds[0],
- joypad_info.joy_idx,
- key);
- }
- return true;
- }
- bool input_mouse_grabbed(void)
- {
- struct rarch_state *p_rarch = &rarch_st;
- return p_rarch->input_driver_grab_mouse_state;
- }
- int16_t button_is_pressed(
- const input_device_driver_t *joypad,
- rarch_joypad_info_t *joypad_info,
- const struct retro_keybind *binds,
- unsigned port, unsigned id)
- {
- /* Auto-binds are per joypad, not per user. */
- const uint64_t joykey = (binds[id].joykey != NO_BTN)
- ? binds[id].joykey : joypad_info->auto_binds[id].joykey;
- const uint32_t joyaxis = (binds[id].joyaxis != AXIS_NONE)
- ? binds[id].joyaxis : joypad_info->auto_binds[id].joyaxis;
- if ((uint16_t)joykey != NO_BTN && joypad->button(
- joypad_info->joy_idx, (uint16_t)joykey))
- return 1;
- if (joyaxis != AXIS_NONE &&
- ((float)abs(joypad->axis(joypad_info->joy_idx, joyaxis))
- / 0x8000) > joypad_info->axis_threshold)
- return 1;
- return 0;
- }
- /**
- * input_joypad_analog:
- * @drv : Input device driver handle.
- * @port : User number.
- * @idx : Analog key index.
- * E.g.:
- * - RETRO_DEVICE_INDEX_ANALOG_LEFT
- * - RETRO_DEVICE_INDEX_ANALOG_RIGHT
- * @ident : Analog key identifier.
- * E.g.:
- * - RETRO_DEVICE_ID_ANALOG_X
- * - RETRO_DEVICE_ID_ANALOG_Y
- * @binds : Binds of user.
- *
- * Gets analog value of analog key identifiers @idx and @ident
- * from user with number @port with provided keybinds (@binds).
- *
- * Returns: analog value on success, otherwise 0.
- **/
- static int16_t input_joypad_analog_button(
- struct rarch_state *p_rarch,
- settings_t *settings,
- const input_device_driver_t *drv,
- rarch_joypad_info_t *joypad_info,
- unsigned port, unsigned idx, unsigned ident,
- const struct retro_keybind *binds)
- {
- int16_t res = 0;
- float normal_mag = 0.0f;
- float input_analog_deadzone = settings->floats.input_analog_deadzone;
- const struct retro_keybind *bind = &binds[ ident ];
- uint32_t axis = (bind->joyaxis == AXIS_NONE)
- ? joypad_info->auto_binds[ident].joyaxis
- : bind->joyaxis;
- /* Analog button. */
- if (input_analog_deadzone)
- {
- int16_t mult = 0;
- if (axis != AXIS_NONE)
- mult = drv->axis(
- joypad_info->joy_idx, axis);
- normal_mag = fabs((1.0f / 0x7fff) * mult);
- }
- res = abs(input_joypad_axis(p_rarch, drv,
- joypad_info->joy_idx, axis, normal_mag));
- /* If the result is zero, it's got a digital button
- * attached to it instead */
- if (res == 0)
- {
- uint16_t key = (bind->joykey == NO_BTN)
- ? joypad_info->auto_binds[ident].joykey
- : bind->joykey;
- if (drv->button(joypad_info->joy_idx, key))
- return 0x7fff;
- return 0;
- }
- return res;
- }
- static int16_t input_joypad_analog_axis(
- struct rarch_state *p_rarch,
- settings_t *settings,
- const input_device_driver_t *drv,
- rarch_joypad_info_t *joypad_info,
- unsigned port, unsigned idx, unsigned ident,
- const struct retro_keybind *binds)
- {
- int16_t res = 0;
- /* Analog sticks. Either RETRO_DEVICE_INDEX_ANALOG_LEFT
- * or RETRO_DEVICE_INDEX_ANALOG_RIGHT */
- unsigned ident_minus = 0;
- unsigned ident_plus = 0;
- unsigned ident_x_minus = 0;
- unsigned ident_x_plus = 0;
- unsigned ident_y_minus = 0;
- unsigned ident_y_plus = 0;
- const struct retro_keybind *bind_minus = NULL;
- const struct retro_keybind *bind_plus = NULL;
- const struct retro_keybind *bind_x_minus = NULL;
- const struct retro_keybind *bind_x_plus = NULL;
- const struct retro_keybind *bind_y_minus = NULL;
- const struct retro_keybind *bind_y_plus = NULL;
- /* Skip analog input with analog_dpad_mode */
- switch (settings->uints.input_analog_dpad_mode[port])
- {
- case ANALOG_DPAD_LSTICK:
- if (idx == RETRO_DEVICE_INDEX_ANALOG_LEFT)
- return 0;
- break;
- case ANALOG_DPAD_RSTICK:
- if (idx == RETRO_DEVICE_INDEX_ANALOG_RIGHT)
- return 0;
- break;
- default:
- break;
- }
- input_conv_analog_id_to_bind_id(idx, ident, ident_minus, ident_plus);
- bind_minus = &binds[ident_minus];
- bind_plus = &binds[ident_plus];
- if (!bind_minus->valid || !bind_plus->valid)
- return 0;
- input_conv_analog_id_to_bind_id(idx,
- RETRO_DEVICE_ID_ANALOG_X, ident_x_minus, ident_x_plus);
- bind_x_minus = &binds[ident_x_minus];
- bind_x_plus = &binds[ident_x_plus];
- if (!bind_x_minus->valid || !bind_x_plus->valid)
- return 0;
- input_conv_analog_id_to_bind_id(idx,
- RETRO_DEVICE_ID_ANALOG_Y, ident_y_minus, ident_y_plus);
- bind_y_minus = &binds[ident_y_minus];
- bind_y_plus = &binds[ident_y_plus];
- if (!bind_y_minus->valid || !bind_y_plus->valid)
- return 0;
- {
- uint32_t axis_minus = (bind_minus->joyaxis == AXIS_NONE)
- ? joypad_info->auto_binds[ident_minus].joyaxis
- : bind_minus->joyaxis;
- uint32_t axis_plus = (bind_plus->joyaxis == AXIS_NONE)
- ? joypad_info->auto_binds[ident_plus].joyaxis
- : bind_plus->joyaxis;
- float input_analog_deadzone =
- settings->floats.input_analog_deadzone;
- float normal_mag = 0.0f;
- /* normalized magnitude of stick actuation, needed for scaled
- * radial deadzone */
- if (input_analog_deadzone)
- {
- float x = 0.0f;
- float y = 0.0f;
- uint32_t x_axis_minus = (bind_x_minus->joyaxis == AXIS_NONE)
- ? joypad_info->auto_binds[ident_x_minus].joyaxis
- : bind_x_minus->joyaxis;
- uint32_t x_axis_plus = (bind_x_plus->joyaxis == AXIS_NONE)
- ? joypad_info->auto_binds[ident_x_plus].joyaxis
- : bind_x_plus->joyaxis;
- uint32_t y_axis_minus = (bind_y_minus->joyaxis == AXIS_NONE)
- ? joypad_info->auto_binds[ident_y_minus].joyaxis
- : bind_y_minus->joyaxis;
- uint32_t y_axis_plus = (bind_y_plus->joyaxis == AXIS_NONE)
- ? joypad_info->auto_binds[ident_y_plus].joyaxis
- : bind_y_plus->joyaxis;
- /* normalized magnitude for radial scaled analog deadzone */
- if (x_axis_plus != AXIS_NONE)
- x = drv->axis(
- joypad_info->joy_idx, x_axis_plus);
- if (x_axis_minus != AXIS_NONE)
- x += drv->axis(joypad_info->joy_idx,
- x_axis_minus);
- if (y_axis_plus != AXIS_NONE)
- y = drv->axis(
- joypad_info->joy_idx, y_axis_plus);
- if (y_axis_minus != AXIS_NONE)
- y += drv->axis(
- joypad_info->joy_idx, y_axis_minus);
- normal_mag = (1.0f / 0x7fff) * sqrt(x * x + y * y);
- }
- res = abs(
- input_joypad_axis(p_rarch,
- drv, joypad_info->joy_idx,
- axis_plus, normal_mag));
- res -= abs(
- input_joypad_axis(p_rarch,
- drv, joypad_info->joy_idx,
- axis_minus, normal_mag));
- }
- if (res == 0)
- {
- uint16_t key_minus = (bind_minus->joykey == NO_BTN)
- ? joypad_info->auto_binds[ident_minus].joykey
- : bind_minus->joykey;
- uint16_t key_plus = (bind_plus->joykey == NO_BTN)
- ? joypad_info->auto_binds[ident_plus].joykey
- : bind_plus->joykey;
- if (drv->button(joypad_info->joy_idx, key_plus))
- res = 0x7fff;
- if (drv->button(joypad_info->joy_idx, key_minus))
- res += -0x7fff;
- }
- return res;
- }
- #ifdef HAVE_MENU
- /**
- * input_mouse_button_raw:
- * @port : Mouse number.
- * @button : Identifier of key (libretro mouse constant).
- *
- * Checks if key (@button) was being pressed by user
- * with mouse number @port.
- *
- * Returns: true (1) if key was pressed, otherwise
- * false (0).
- **/
- static bool input_mouse_button_raw(
- struct rarch_state *p_rarch,
- settings_t *settings,
- unsigned port, unsigned id)
- {
- rarch_joypad_info_t joypad_info;
- input_driver_t *current_input = p_rarch->current_input;
- #ifdef HAVE_MFI
- const input_device_driver_t
- *sec_joypad = p_rarch->sec_joypad;
- #else
- const input_device_driver_t
- *sec_joypad = NULL;
- #endif
- /*ignore axes*/
- if (id == RETRO_DEVICE_ID_MOUSE_X || id == RETRO_DEVICE_ID_MOUSE_Y)
- return false;
- joypad_info.axis_threshold = p_rarch->input_driver_axis_threshold;
- joypad_info.joy_idx = settings->uints.input_joypad_map[port];
- joypad_info.auto_binds = input_autoconf_binds[joypad_info.joy_idx];
- if (current_input->input_state)
- return current_input->input_state(
- p_rarch->current_input_data,
- p_rarch->joypad,
- sec_joypad,
- &joypad_info,
- p_rarch->libretro_input_binds,
- p_rarch->keyboard_mapping_blocked,
- port,
- RETRO_DEVICE_MOUSE, 0, id);
- return false;
- }
- #endif
- void input_pad_connect(unsigned port, input_device_driver_t *driver)
- {
- struct rarch_state *p_rarch = &rarch_st;
- if (port >= MAX_USERS || !driver)
- {
- RARCH_ERR("[Input]: input_pad_connect: bad parameters\n");
- return;
- }
- if (p_rarch->pad_connection_listener)
- p_rarch->pad_connection_listener->connected(port, driver);
- input_autoconfigure_connect(driver->name(port), NULL, driver->ident,
- port, 0, 0);
- }
- #ifdef HAVE_HID
- const void *hid_driver_get_data(void)
- {
- struct rarch_state *p_rarch = &rarch_st;
- return p_rarch->hid_data;
- }
- /* This is only to be called after we've invoked free() on the
- * HID driver; the memory will have already been freed, so we need to
- * reset the pointer.
- */
- void hid_driver_reset_data(void)
- {
- struct rarch_state *p_rarch = &rarch_st;
- p_rarch->hid_data = NULL;
- }
- /**
- * config_get_hid_driver_options:
- *
- * Get an enumerated list of all HID driver names, separated by '|'.
- *
- * Returns: string listing of all HID driver names, separated by '|'.
- **/
- const char* config_get_hid_driver_options(void)
- {
- return char_list_new_special(STRING_LIST_INPUT_HID_DRIVERS, NULL);
- }
- /**
- * input_hid_init_first:
- *
- * Finds first suitable HID driver and initializes.
- *
- * Returns: HID driver if found, otherwise NULL.
- **/
- const hid_driver_t *input_hid_init_first(void)
- {
- unsigned i;
- struct rarch_state *p_rarch = &rarch_st;
- for (i = 0; hid_drivers[i]; i++)
- {
- p_rarch->hid_data = hid_drivers[i]->init();
- if (p_rarch->hid_data)
- {
- RARCH_LOG("[Input]: Found HID driver: \"%s\".\n",
- hid_drivers[i]->ident);
- return hid_drivers[i];
- }
- }
- return NULL;
- }
- #endif
- /**
- * input_keyboard_line_event:
- * @state : Input keyboard line handle.
- * @character : Inputted character.
- *
- * Called on every keyboard character event.
- *
- * Returns: true (1) on success, otherwise false (0).
- **/
- static bool input_keyboard_line_event(
- struct rarch_state *p_rarch,
- input_keyboard_line_t *state, uint32_t character)
- {
- char array[2];
- bool ret = false;
- const char *word = NULL;
- char c = (character >= 128) ? '?' : character;
- /* Treat extended chars as ? as we cannot support
- * printable characters for unicode stuff. */
- if (c == '\r' || c == '\n')
- {
- state->cb(state->userdata, state->buffer);
- array[0] = c;
- array[1] = 0;
- ret = true;
- word = array;
- }
- else if (c == '\b' || c == '\x7f') /* 0x7f is ASCII for del */
- {
- if (state->ptr)
- {
- unsigned i;
- for (i = 0; i < p_rarch->osk_last_codepoint_len; i++)
- {
- memmove(state->buffer + state->ptr - 1,
- state->buffer + state->ptr,
- state->size - state->ptr + 1);
- state->ptr--;
- state->size--;
- }
- word = state->buffer;
- }
- }
- else if (ISPRINT(c))
- {
- /* Handle left/right here when suitable */
- char *newbuf = (char*)
- realloc(state->buffer, state->size + 2);
- if (!newbuf)
- return false;
- memmove(newbuf + state->ptr + 1,
- newbuf + state->ptr,
- state->size - state->ptr + 1);
- newbuf[state->ptr] = c;
- state->ptr++;
- state->size++;
- newbuf[state->size] = '\0';
- state->buffer = newbuf;
- array[0] = c;
- array[1] = 0;
- word = array;
- }
- if (word)
- {
- /* OSK - update last character */
- if (word[0] == 0)
- {
- p_rarch->osk_last_codepoint = 0;
- p_rarch->osk_last_codepoint_len = 0;
- }
- else
- osk_update_last_codepoint(
- &p_rarch->osk_last_codepoint,
- &p_rarch->osk_last_codepoint_len,
- word);
- }
- return ret;
- }
- #ifdef HAVE_MENU
- static bool input_keyboard_line_append(
- struct input_keyboard_line *keyboard_line,
- const char *word)
- {
- unsigned i = 0;
- unsigned len = (unsigned)strlen(word);
- char *newbuf = (char*)realloc(
- keyboard_line->buffer,
- keyboard_line->size + len * 2);
- if (!newbuf)
- return false;
- memmove(
- newbuf + keyboard_line->ptr + len,
- newbuf + keyboard_line->ptr,
- keyboard_line->size - keyboard_line->ptr + len);
- for (i = 0; i < len; i++)
- {
- newbuf[keyboard_line->ptr]= word[i];
- keyboard_line->ptr++;
- keyboard_line->size++;
- }
- newbuf[keyboard_line->size] = '\0';
- keyboard_line->buffer = newbuf;
- return true;
- }
- /**
- * input_keyboard_start_line:
- * @userdata : Userdata.
- * @cb : Line complete callback function.
- *
- * Sets function pointer for keyboard line handle.
- *
- * The underlying buffer can be reallocated at any time
- * (or be NULL), but the pointer to it remains constant
- * throughout the objects lifetime.
- *
- * Returns: underlying buffer of the keyboard line.
- **/
- static const char **input_keyboard_start_line(
- void *userdata,
- struct input_keyboard_line *keyboard_line,
- input_keyboard_line_complete_t cb)
- {
- keyboard_line->buffer = NULL;
- keyboard_line->ptr = 0;
- keyboard_line->size = 0;
- keyboard_line->cb = cb;
- keyboard_line->userdata = userdata;
- keyboard_line->enabled = true;
- return (const char**)&keyboard_line->buffer;
- }
- #endif
- /**
- * input_keyboard_event:
- * @down : Keycode was pressed down?
- * @code : Keycode.
- * @character : Character inputted.
- * @mod : TODO/FIXME: ???
- *
- * Keyboard event utils. Called by drivers when keyboard events are fired.
- * This interfaces with the global system driver struct and libretro callbacks.
- **/
- void input_keyboard_event(bool down, unsigned code,
- uint32_t character, uint16_t mod, unsigned device)
- {
- static bool deferred_wait_keys;
- struct rarch_state *p_rarch = &rarch_st;
- #ifdef HAVE_ACCESSIBILITY
- #ifdef HAVE_MENU
- if (menu_input_dialog_get_display_kb()
- && down && is_accessibility_enabled(p_rarch))
- {
- if (code != 303 && code != 0)
- {
- char* say_char = (char*)malloc(sizeof(char)+1);
- if (say_char)
- {
- char c = (char) character;
- *say_char = c;
- if (character == 127)
- accessibility_speak_priority(p_rarch, "backspace", 10);
- else if (c == '`')
- accessibility_speak_priority(p_rarch, "left quote", 10);
- else if (c == '`')
- accessibility_speak_priority(p_rarch, "tilde", 10);
- else if (c == '!')
- accessibility_speak_priority(p_rarch, "exclamation point", 10);
- else if (c == '@')
- accessibility_speak_priority(p_rarch, "at sign", 10);
- else if (c == '#')
- accessibility_speak_priority(p_rarch, "hash sign", 10);
- else if (c == '$')
- accessibility_speak_priority(p_rarch, "dollar sign", 10);
- else if (c == '%')
- accessibility_speak_priority(p_rarch, "percent sign", 10);
- else if (c == '^')
- accessibility_speak_priority(p_rarch, "carrot", 10);
- else if (c == '&')
- accessibility_speak_priority(p_rarch, "ampersand", 10);
- else if (c == '*')
- accessibility_speak_priority(p_rarch, "asterisk", 10);
- else if (c == '(')
- accessibility_speak_priority(p_rarch, "left bracket", 10);
- else if (c == ')')
- accessibility_speak_priority(p_rarch, "right bracket", 10);
- else if (c == '-')
- accessibility_speak_priority(p_rarch, "minus", 10);
- else if (c == '_')
- accessibility_speak_priority(p_rarch, "underscore", 10);
- else if (c == '=')
- accessibility_speak_priority(p_rarch, "equals", 10);
- else if (c == '+')
- accessibility_speak_priority(p_rarch, "plus", 10);
- else if (c == '[')
- accessibility_speak_priority(p_rarch, "left square bracket", 10);
- else if (c == '{')
- accessibility_speak_priority(p_rarch, "left curl bracket", 10);
- else if (c == ']')
- accessibility_speak_priority(p_rarch, "right square bracket", 10);
- else if (c == '}')
- accessibility_speak_priority(p_rarch, "right curl bracket", 10);
- else if (c == '\\')
- accessibility_speak_priority(p_rarch, "back slash", 10);
- else if (c == '|')
- accessibility_speak_priority(p_rarch, "pipe", 10);
- else if (c == ';')
- accessibility_speak_priority(p_rarch, "semicolon", 10);
- else if (c == ':')
- accessibility_speak_priority(p_rarch, "colon", 10);
- else if (c == '\'')
- accessibility_speak_priority(p_rarch, "single quote", 10);
- else if (c == '\"')
- accessibility_speak_priority(p_rarch, "double quote", 10);
- else if (c == ',')
- accessibility_speak_priority(p_rarch, "comma", 10);
- else if (c == '<')
- accessibility_speak_priority(p_rarch, "left angle bracket", 10);
- else if (c == '.')
- accessibility_speak_priority(p_rarch, "period", 10);
- else if (c == '>')
- accessibility_speak_priority(p_rarch, "right angle bracket", 10);
- else if (c == '/')
- accessibility_speak_priority(p_rarch, "front slash", 10);
- else if (c == '?')
- accessibility_speak_priority(p_rarch, "question mark", 10);
- else if (c == ' ')
- accessibility_speak_priority(p_rarch, "space", 10);
- else if (character != 0)
- accessibility_speak_priority(p_rarch, say_char, 10);
- free(say_char);
- }
- }
- }
- #endif
- #endif
- if (deferred_wait_keys)
- {
- if (down)
- return;
- p_rarch->keyboard_press_cb = NULL;
- p_rarch->keyboard_press_data = NULL;
- p_rarch->keyboard_mapping_blocked = false;
- deferred_wait_keys = false;
- }
- else if (p_rarch->keyboard_press_cb)
- {
- if (!down || code == RETROK_UNKNOWN)
- return;
- if (p_rarch->keyboard_press_cb(p_rarch->keyboard_press_data, code))
- return;
- deferred_wait_keys = true;
- }
- else if (p_rarch->keyboard_line.enabled)
- {
- if (!down)
- return;
- switch (device)
- {
- case RETRO_DEVICE_POINTER:
- if (code != 0x12d)
- character = (char)code;
- /* fall-through */
- default:
- if (!input_keyboard_line_event(p_rarch,
- &p_rarch->keyboard_line, character))
- return;
- break;
- }
- /* Line is complete, can free it now. */
- if (p_rarch->keyboard_line.buffer)
- free(p_rarch->keyboard_line.buffer);
- p_rarch->keyboard_line.buffer = NULL;
- p_rarch->keyboard_line.ptr = 0;
- p_rarch->keyboard_line.size = 0;
- p_rarch->keyboard_line.cb = NULL;
- p_rarch->keyboard_line.userdata = NULL;
- p_rarch->keyboard_line.enabled = false;
- /* Unblock all hotkeys. */
- p_rarch->keyboard_mapping_blocked = false;
- }
- else
- {
- if (code == RETROK_UNKNOWN)
- return;
- /* Block hotkey + RetroPad mapped keyboard key events,
- * but not with game focus and from keyboard device type */
- if (!p_rarch->game_focus_state.enabled)
- {
- input_mapper_t *handle = p_rarch->input_driver_mapper;
- if (BIT512_GET(
- p_rarch->keyboard_mapping_bits, code))
- if (!(handle && MAPPER_GET_KEY(handle, code)))
- return;
- }
- {
- retro_keyboard_event_t *key_event = &p_rarch->runloop_key_event;
- if (*key_event)
- (*key_event)(down, code, character, mod);
- }
- }
- }
- static bool input_config_bind_map_get_valid(unsigned i)
- {
- const struct input_bind_map *keybind =
- (const struct input_bind_map*)INPUT_CONFIG_BIND_MAP_GET(i);
- if (!keybind)
- return false;
- return keybind->valid;
- }
- unsigned input_config_bind_map_get_meta(unsigned i)
- {
- const struct input_bind_map *keybind =
- (const struct input_bind_map*)INPUT_CONFIG_BIND_MAP_GET(i);
- if (!keybind)
- return 0;
- return keybind->meta;
- }
- const char *input_config_bind_map_get_base(unsigned i)
- {
- const struct input_bind_map *keybind =
- (const struct input_bind_map*)INPUT_CONFIG_BIND_MAP_GET(i);
- if (!keybind)
- return NULL;
- return keybind->base;
- }
- const char *input_config_bind_map_get_desc(unsigned i)
- {
- const struct input_bind_map *keybind =
- (const struct input_bind_map*)INPUT_CONFIG_BIND_MAP_GET(i);
- if (!keybind)
- return NULL;
- return msg_hash_to_str(keybind->desc);
- }
- uint8_t input_config_bind_map_get_retro_key(unsigned i)
- {
- const struct input_bind_map *keybind =
- (const struct input_bind_map*)INPUT_CONFIG_BIND_MAP_GET(i);
- if (!keybind)
- return 0;
- return keybind->retro_key;
- }
- static void input_config_parse_key(
- config_file_t *conf,
- const char *prefix, const char *btn,
- struct retro_keybind *bind)
- {
- char key[64];
- struct config_entry_list *entry = NULL;
- key[0] = '\0';
- fill_pathname_join_delim(key, prefix, btn, '_', sizeof(key));
- if (
- (entry = config_get_entry(conf, key))
- && (!string_is_empty(entry->value))
- )
- bind->key = input_config_translate_str_to_rk(entry->value);
- /* Store mapping bit */
- input_keyboard_mapping_bits(1, bind->key);
- }
- static const char *input_config_get_prefix(unsigned user, bool meta)
- {
- static const char *bind_user_prefix[MAX_USERS] = {
- "input_player1",
- "input_player2",
- "input_player3",
- "input_player4",
- "input_player5",
- "input_player6",
- "input_player7",
- "input_player8",
- "input_player9",
- "input_player10",
- "input_player11",
- "input_player12",
- "input_player13",
- "input_player14",
- "input_player15",
- "input_player16",
- };
- const char *prefix = bind_user_prefix[user];
- if (user == 0)
- return meta ? "input" : prefix;
- if (!meta)
- return prefix;
- /* Don't bother with meta bind for anyone else than first user. */
- return NULL;
- }
- /**
- * input_config_translate_str_to_rk:
- * @str : String to translate to key ID.
- *
- * Translates tring representation to key identifier.
- *
- * Returns: key identifier.
- **/
- enum retro_key input_config_translate_str_to_rk(const char *str)
- {
- size_t i;
- if (strlen(str) == 1 && ISALPHA((int)*str))
- return (enum retro_key)(RETROK_a + (TOLOWER((int)*str) - (int)'a'));
- for (i = 0; input_config_key_map[i].str; i++)
- {
- if (string_is_equal_noncase(input_config_key_map[i].str, str))
- return input_config_key_map[i].key;
- }
- RARCH_WARN("[Input]: Key name \"%s\" not found.\n", str);
- return RETROK_UNKNOWN;
- }
- /**
- * input_config_translate_str_to_bind_id:
- * @str : String to translate to bind ID.
- *
- * Translate string representation to bind ID.
- *
- * Returns: Bind ID value on success, otherwise
- * RARCH_BIND_LIST_END on not found.
- **/
- unsigned input_config_translate_str_to_bind_id(const char *str)
- {
- unsigned i;
- for (i = 0; input_config_bind_map[i].valid; i++)
- if (string_is_equal(str, input_config_bind_map[i].base))
- return i;
- return RARCH_BIND_LIST_END;
- }
- static void parse_hat(struct retro_keybind *bind, const char *str)
- {
- uint16_t hat_dir = 0;
- char *dir = NULL;
- uint16_t hat = strtoul(str, &dir, 0);
- if (!dir)
- {
- RARCH_WARN("[Input]: Found invalid hat in config!\n");
- return;
- }
- if ( dir[0] == 'u'
- && dir[1] == 'p'
- && dir[2] == '\0'
- )
- hat_dir = HAT_UP_MASK;
- else if ( dir[0] == 'd'
- && dir[1] == 'o'
- && dir[2] == 'w'
- && dir[3] == 'n'
- && dir[4] == '\0'
- )
- hat_dir = HAT_DOWN_MASK;
- else if ( dir[0] == 'l'
- && dir[1] == 'e'
- && dir[2] == 'f'
- && dir[3] == 't'
- && dir[4] == '\0'
- )
- hat_dir = HAT_LEFT_MASK;
- else if ( dir[0] == 'r'
- && dir[1] == 'i'
- && dir[2] == 'g'
- && dir[3] == 'h'
- && dir[4] == 't'
- && dir[5] == '\0'
- )
- hat_dir = HAT_RIGHT_MASK;
- if (hat_dir)
- bind->joykey = HAT_MAP(hat, hat_dir);
- }
- static void input_config_parse_joy_button(
- config_file_t *conf, const char *prefix,
- const char *btn, struct retro_keybind *bind)
- {
- char str[256];
- char tmp[64];
- char key[64];
- char key_label[64];
- struct config_entry_list *tmp_a = NULL;
- str[0] = tmp[0] = key[0] = key_label[0] = '\0';
- fill_pathname_join_delim(str, prefix, btn,
- '_', sizeof(str));
- fill_pathname_join_delim(key, str,
- "btn", '_', sizeof(key));
- fill_pathname_join_delim(key_label, str,
- "btn_label", '_', sizeof(key_label));
- if (config_get_array(conf, key, tmp, sizeof(tmp)))
- {
- btn = tmp;
- if ( btn[0] == 'n'
- && btn[1] == 'u'
- && btn[2] == 'l'
- && btn[3] == '\0'
- )
- bind->joykey = NO_BTN;
- else
- {
- if (*btn == 'h')
- {
- const char *str = btn + 1;
- if (str && ISDIGIT((int)*str))
- parse_hat(bind, str);
- }
- else
- bind->joykey = strtoull(tmp, NULL, 0);
- }
- }
- tmp_a = config_get_entry(conf, key_label);
- if (tmp_a && !string_is_empty(tmp_a->value))
- {
- if (!string_is_empty(bind->joykey_label))
- free(bind->joykey_label);
- bind->joykey_label = strdup(tmp_a->value);
- }
- }
- static void input_config_parse_joy_axis(
- config_file_t *conf, const char *prefix,
- const char *axis, struct retro_keybind *bind)
- {
- char str[256];
- char tmp[64];
- char key[64];
- char key_label[64];
- struct config_entry_list *tmp_a = NULL;
- str[0] = tmp[0] = key[0] = key_label[0] = '\0';
- fill_pathname_join_delim(str, prefix, axis,
- '_', sizeof(str));
- fill_pathname_join_delim(key, str,
- "axis", '_', sizeof(key));
- fill_pathname_join_delim(key_label, str,
- "axis_label", '_', sizeof(key_label));
- if (config_get_array(conf, key, tmp, sizeof(tmp)))
- {
- if ( tmp[0] == 'n'
- && tmp[1] == 'u'
- && tmp[2] == 'l'
- && tmp[3] == '\0'
- )
- bind->joyaxis = AXIS_NONE;
- else if (strlen(tmp) >= 2 && (*tmp == '+' || *tmp == '-'))
- {
- int i_axis = (int)strtol(tmp + 1, NULL, 0);
- if (*tmp == '+')
- bind->joyaxis = AXIS_POS(i_axis);
- else
- bind->joyaxis = AXIS_NEG(i_axis);
- }
- /* Ensure that D-pad emulation doesn't screw this over. */
- bind->orig_joyaxis = bind->joyaxis;
- }
- tmp_a = config_get_entry(conf, key_label);
- if (tmp_a && (!string_is_empty(tmp_a->value)))
- {
- if (bind->joyaxis_label &&
- !string_is_empty(bind->joyaxis_label))
- free(bind->joyaxis_label);
- bind->joyaxis_label = strdup(tmp_a->value);
- }
- }
- static void input_config_parse_mouse_button(
- config_file_t *conf, const char *prefix,
- const char *btn, struct retro_keybind *bind)
- {
- int val;
- char str[256];
- char tmp[64];
- char key[64];
- str[0] = tmp[0] = key[0] = '\0';
- fill_pathname_join_delim(str, prefix, btn,
- '_', sizeof(str));
- fill_pathname_join_delim(key, str,
- "mbtn", '_', sizeof(key));
- if (config_get_array(conf, key, tmp, sizeof(tmp)))
- {
- bind->mbutton = NO_BTN;
- if (tmp[0]=='w')
- {
- switch (tmp[1])
- {
- case 'u':
- bind->mbutton = RETRO_DEVICE_ID_MOUSE_WHEELUP;
- break;
- case 'd':
- bind->mbutton = RETRO_DEVICE_ID_MOUSE_WHEELDOWN;
- break;
- case 'h':
- switch (tmp[2])
- {
- case 'u':
- bind->mbutton = RETRO_DEVICE_ID_MOUSE_HORIZ_WHEELUP;
- break;
- case 'd':
- bind->mbutton = RETRO_DEVICE_ID_MOUSE_HORIZ_WHEELDOWN;
- break;
- }
- break;
- }
- }
- else
- {
- val = atoi(tmp);
- switch (val)
- {
- case 1:
- bind->mbutton = RETRO_DEVICE_ID_MOUSE_LEFT;
- break;
- case 2:
- bind->mbutton = RETRO_DEVICE_ID_MOUSE_RIGHT;
- break;
- case 3:
- bind->mbutton = RETRO_DEVICE_ID_MOUSE_MIDDLE;
- break;
- case 4:
- bind->mbutton = RETRO_DEVICE_ID_MOUSE_BUTTON_4;
- break;
- case 5:
- bind->mbutton = RETRO_DEVICE_ID_MOUSE_BUTTON_5;
- break;
- }
- }
- }
- }
- static void input_config_get_bind_string_joykey(
- settings_t *settings,
- char *buf, const char *prefix,
- const struct retro_keybind *bind, size_t size)
- {
- bool label_show =
- settings->bools.input_descriptor_label_show;
- if (GET_HAT_DIR(bind->joykey))
- {
- if (bind->joykey_label &&
- !string_is_empty(bind->joykey_label) && label_show)
- fill_pathname_join_delim_concat(buf, prefix,
- bind->joykey_label, ' ', " (hat)", size);
- else
- {
- const char *dir = "?";
- switch (GET_HAT_DIR(bind->joykey))
- {
- case HAT_UP_MASK:
- dir = "up";
- break;
- case HAT_DOWN_MASK:
- dir = "down";
- break;
- case HAT_LEFT_MASK:
- dir = "left";
- break;
- case HAT_RIGHT_MASK:
- dir = "right";
- break;
- default:
- break;
- }
- snprintf(buf, size, "%sHat #%u %s (%s)", prefix,
- (unsigned)GET_HAT(bind->joykey), dir,
- msg_hash_to_str(MENU_ENUM_LABEL_VALUE_NOT_AVAILABLE));
- }
- }
- else
- {
- if (bind->joykey_label &&
- !string_is_empty(bind->joykey_label) && label_show)
- fill_pathname_join_delim_concat(buf, prefix,
- bind->joykey_label, ' ', " (btn)", size);
- else
- snprintf(buf, size, "%s%u (%s)", prefix, (unsigned)bind->joykey,
- msg_hash_to_str(MENU_ENUM_LABEL_VALUE_NOT_AVAILABLE));
- }
- }
- static void input_config_get_bind_string_joyaxis(
- settings_t *settings,
- char *buf, const char *prefix,
- const struct retro_keybind *bind, size_t size)
- {
- bool input_descriptor_label_show =
- settings->bools.input_descriptor_label_show;
- if (bind->joyaxis_label &&
- !string_is_empty(bind->joyaxis_label)
- && input_descriptor_label_show)
- fill_pathname_join_delim_concat(buf, prefix,
- bind->joyaxis_label, ' ', " (axis)", size);
- else
- {
- unsigned axis = 0;
- char dir = '\0';
- if (AXIS_NEG_GET(bind->joyaxis) != AXIS_DIR_NONE)
- {
- dir = '-';
- axis = AXIS_NEG_GET(bind->joyaxis);
- }
- else if (AXIS_POS_GET(bind->joyaxis) != AXIS_DIR_NONE)
- {
- dir = '+';
- axis = AXIS_POS_GET(bind->joyaxis);
- }
- snprintf(buf, size, "%s%c%u (%s)", prefix, dir, axis,
- msg_hash_to_str(MENU_ENUM_LABEL_VALUE_NOT_AVAILABLE));
- }
- }
- void input_config_get_bind_string(char *buf,
- const struct retro_keybind *bind,
- const struct retro_keybind *auto_bind,
- size_t size)
- {
- int delim = 0;
- struct rarch_state *p_rarch = &rarch_st;
- *buf = '\0';
- if (bind && bind->joykey != NO_BTN)
- input_config_get_bind_string_joykey(
- p_rarch->configuration_settings, buf, "", bind, size);
- else if (bind && bind->joyaxis != AXIS_NONE)
- input_config_get_bind_string_joyaxis(
- p_rarch->configuration_settings, buf, "", bind, size);
- else if (auto_bind && auto_bind->joykey != NO_BTN)
- input_config_get_bind_string_joykey(
- p_rarch->configuration_settings, buf, "Auto: ", auto_bind, size);
- else if (auto_bind && auto_bind->joyaxis != AXIS_NONE)
- input_config_get_bind_string_joyaxis(
- p_rarch->configuration_settings, buf, "Auto: ", auto_bind, size);
- if (*buf)
- delim = 1;
- #ifndef RARCH_CONSOLE
- {
- char key[64];
- key[0] = '\0';
- input_keymaps_translate_rk_to_str(bind->key, key, sizeof(key));
- if ( key[0] == 'n'
- && key[1] == 'u'
- && key[2] == 'l'
- && key[3] == '\0'
- )
- *key = '\0';
- /*empty?*/
- if (*key != '\0')
- {
- char keybuf[64];
- keybuf[0] = '\0';
- if (delim)
- strlcat(buf, ", ", size);
- snprintf(keybuf, sizeof(keybuf),
- msg_hash_to_str(MENU_ENUM_LABEL_VALUE_INPUT_KEY), key);
- strlcat(buf, keybuf, size);
- delim = 1;
- }
- }
- #endif
- if (bind->mbutton != NO_BTN)
- {
- int tag = 0;
- switch (bind->mbutton)
- {
- case RETRO_DEVICE_ID_MOUSE_LEFT:
- tag = MENU_ENUM_LABEL_VALUE_INPUT_MOUSE_LEFT;
- break;
- case RETRO_DEVICE_ID_MOUSE_RIGHT:
- tag = MENU_ENUM_LABEL_VALUE_INPUT_MOUSE_RIGHT;
- break;
- case RETRO_DEVICE_ID_MOUSE_MIDDLE:
- tag = MENU_ENUM_LABEL_VALUE_INPUT_MOUSE_MIDDLE;
- break;
- case RETRO_DEVICE_ID_MOUSE_BUTTON_4:
- tag = MENU_ENUM_LABEL_VALUE_INPUT_MOUSE_BUTTON4;
- break;
- case RETRO_DEVICE_ID_MOUSE_BUTTON_5:
- tag = MENU_ENUM_LABEL_VALUE_INPUT_MOUSE_BUTTON5;
- break;
- case RETRO_DEVICE_ID_MOUSE_WHEELUP:
- tag = MENU_ENUM_LABEL_VALUE_INPUT_MOUSE_WHEEL_UP;
- break;
- case RETRO_DEVICE_ID_MOUSE_WHEELDOWN:
- tag = MENU_ENUM_LABEL_VALUE_INPUT_MOUSE_WHEEL_DOWN;
- break;
- case RETRO_DEVICE_ID_MOUSE_HORIZ_WHEELUP:
- tag = MENU_ENUM_LABEL_VALUE_INPUT_MOUSE_HORIZ_WHEEL_UP;
- break;
- case RETRO_DEVICE_ID_MOUSE_HORIZ_WHEELDOWN:
- tag = MENU_ENUM_LABEL_VALUE_INPUT_MOUSE_HORIZ_WHEEL_DOWN;
- break;
- }
- if (tag != 0)
- {
- if (delim)
- strlcat(buf, ", ", size);
- strlcat(buf, msg_hash_to_str((enum msg_hash_enums)tag), size);
- }
- }
- /*completely empty?*/
- if (*buf == '\0')
- strlcat(buf, "---", size);
- }
- /* input_device_info wrappers START */
- unsigned input_config_get_device_count(void)
- {
- struct rarch_state *p_rarch = &rarch_st;
- unsigned num_devices;
- for (num_devices = 0; num_devices < MAX_INPUT_DEVICES; ++num_devices)
- {
- if (string_is_empty(p_rarch->input_device_info[num_devices].name))
- break;
- }
- return num_devices;
- }
- /* Adds an index to devices with the same name,
- * so they can be uniquely identified in the
- * frontend */
- static void input_config_reindex_device_names(void)
- {
- unsigned i;
- unsigned j;
- unsigned name_index;
- /* Reset device name indices */
- for (i = 0; i < MAX_INPUT_DEVICES; i++)
- input_config_set_device_name_index(i, 0);
- /* Scan device names */
- for (i = 0; i < MAX_INPUT_DEVICES; i++)
- {
- const char *device_name = input_config_get_device_name(i);
- /* If current device name is empty, or a non-zero
- * name index has already been assigned, continue
- * to the next device */
- if (string_is_empty(device_name) ||
- (input_config_get_device_name_index(i) != 0))
- continue;
- /* > Uniquely named devices have a name index
- * of 0
- * > Devices with the same name have a name
- * index starting from 1 */
- name_index = 1;
- /* Loop over all devices following the current
- * selection */
- for (j = i + 1; j < MAX_INPUT_DEVICES; j++)
- {
- const char *next_device_name = input_config_get_device_name(j);
- if (string_is_empty(next_device_name))
- continue;
- /* Check if names match */
- if (string_is_equal(device_name, next_device_name))
- {
- /* If this is the first match, set a starting
- * index for the current device selection */
- if (input_config_get_device_name_index(i) == 0)
- input_config_set_device_name_index(i, name_index++);
- /* Set name index for the next device
- * (will keep incrementing as more matches
- * are found) */
- input_config_set_device_name_index(j, name_index++);
- }
- }
- }
- }
- /* > Get input_device_info */
- const char *input_config_get_device_name(unsigned port)
- {
- struct rarch_state *p_rarch = &rarch_st;
- if (string_is_empty(p_rarch->input_device_info[port].name))
- return NULL;
- return p_rarch->input_device_info[port].name;
- }
- const char *input_config_get_device_display_name(unsigned port)
- {
- struct rarch_state *p_rarch = &rarch_st;
- if (string_is_empty(p_rarch->input_device_info[port].display_name))
- return NULL;
- return p_rarch->input_device_info[port].display_name;
- }
- const char *input_config_get_device_config_path(unsigned port)
- {
- struct rarch_state *p_rarch = &rarch_st;
- if (string_is_empty(p_rarch->input_device_info[port].config_path))
- return NULL;
- return p_rarch->input_device_info[port].config_path;
- }
- const char *input_config_get_device_config_name(unsigned port)
- {
- struct rarch_state *p_rarch = &rarch_st;
- if (string_is_empty(p_rarch->input_device_info[port].config_name))
- return NULL;
- return p_rarch->input_device_info[port].config_name;
- }
- const char *input_config_get_device_joypad_driver(unsigned port)
- {
- struct rarch_state *p_rarch = &rarch_st;
- if (string_is_empty(p_rarch->input_device_info[port].joypad_driver))
- return NULL;
- return p_rarch->input_device_info[port].joypad_driver;
- }
- uint16_t input_config_get_device_vid(unsigned port)
- {
- struct rarch_state *p_rarch = &rarch_st;
- return p_rarch->input_device_info[port].vid;
- }
- uint16_t input_config_get_device_pid(unsigned port)
- {
- struct rarch_state *p_rarch = &rarch_st;
- return p_rarch->input_device_info[port].pid;
- }
- bool input_config_get_device_autoconfigured(unsigned port)
- {
- struct rarch_state *p_rarch = &rarch_st;
- return p_rarch->input_device_info[port].autoconfigured;
- }
- unsigned input_config_get_device_name_index(unsigned port)
- {
- struct rarch_state *p_rarch = &rarch_st;
- return p_rarch->input_device_info[port].name_index;
- }
- /* TODO/FIXME: This is required by linuxraw_joypad.c
- * and parport_joypad.c. These input drivers should
- * be refactored such that this dubious low-level
- * access is not required */
- char *input_config_get_device_name_ptr(unsigned port)
- {
- struct rarch_state *p_rarch = &rarch_st;
- return p_rarch->input_device_info[port].name;
- }
- size_t input_config_get_device_name_size(unsigned port)
- {
- struct rarch_state *p_rarch = &rarch_st;
- return sizeof(p_rarch->input_device_info[port].name);
- }
- /* > Set input_device_info */
- void input_config_set_device_name(unsigned port, const char *name)
- {
- struct rarch_state *p_rarch = &rarch_st;
- if (string_is_empty(name))
- return;
- strlcpy(p_rarch->input_device_info[port].name, name,
- sizeof(p_rarch->input_device_info[port].name));
- input_config_reindex_device_names();
- }
- void input_config_set_device_display_name(unsigned port, const char *name)
- {
- struct rarch_state *p_rarch = &rarch_st;
- if (!string_is_empty(name))
- strlcpy(p_rarch->input_device_info[port].display_name, name,
- sizeof(p_rarch->input_device_info[port].display_name));
- }
- void input_config_set_device_config_path(unsigned port, const char *path)
- {
- if (!string_is_empty(path))
- {
- char parent_dir_name[128];
- struct rarch_state *p_rarch = &rarch_st;
- parent_dir_name[0] = '\0';
- if (fill_pathname_parent_dir_name(parent_dir_name,
- path, sizeof(parent_dir_name)))
- fill_pathname_join(p_rarch->input_device_info[port].config_path,
- parent_dir_name, path_basename(path),
- sizeof(p_rarch->input_device_info[port].config_path));
- }
- }
- void input_config_set_device_config_name(unsigned port, const char *name)
- {
- struct rarch_state *p_rarch = &rarch_st;
- if (!string_is_empty(name))
- strlcpy(p_rarch->input_device_info[port].config_name, name,
- sizeof(p_rarch->input_device_info[port].config_name));
- }
- void input_config_set_device_joypad_driver(unsigned port, const char *driver)
- {
- struct rarch_state *p_rarch = &rarch_st;
- if (!string_is_empty(driver))
- strlcpy(p_rarch->input_device_info[port].joypad_driver, driver,
- sizeof(p_rarch->input_device_info[port].joypad_driver));
- }
- void input_config_set_device_vid(unsigned port, uint16_t vid)
- {
- struct rarch_state *p_rarch = &rarch_st;
- p_rarch->input_device_info[port].vid = vid;
- }
- void input_config_set_device_pid(unsigned port, uint16_t pid)
- {
- struct rarch_state *p_rarch = &rarch_st;
- p_rarch->input_device_info[port].pid = pid;
- }
- void input_config_set_device_autoconfigured(unsigned port, bool autoconfigured)
- {
- struct rarch_state *p_rarch = &rarch_st;
- p_rarch->input_device_info[port].autoconfigured = autoconfigured;
- }
- void input_config_set_device_name_index(unsigned port, unsigned name_index)
- {
- struct rarch_state *p_rarch = &rarch_st;
- p_rarch->input_device_info[port].name_index = name_index;
- }
- /* > Clear input_device_info */
- void input_config_clear_device_name(unsigned port)
- {
- struct rarch_state *p_rarch = &rarch_st;
- p_rarch->input_device_info[port].name[0] = '\0';
- input_config_reindex_device_names();
- }
- void input_config_clear_device_display_name(unsigned port)
- {
- struct rarch_state *p_rarch = &rarch_st;
- p_rarch->input_device_info[port].display_name[0] = '\0';
- }
- void input_config_clear_device_config_path(unsigned port)
- {
- struct rarch_state *p_rarch = &rarch_st;
- p_rarch->input_device_info[port].config_path[0] = '\0';
- }
- void input_config_clear_device_config_name(unsigned port)
- {
- struct rarch_state *p_rarch = &rarch_st;
- p_rarch->input_device_info[port].config_name[0] = '\0';
- }
- void input_config_clear_device_joypad_driver(unsigned port)
- {
- struct rarch_state *p_rarch = &rarch_st;
- p_rarch->input_device_info[port].joypad_driver[0] = '\0';
- }
- /* input_device_info wrappers END */
- unsigned *input_config_get_device_ptr(unsigned port)
- {
- struct rarch_state *p_rarch = &rarch_st;
- settings_t *settings = p_rarch->configuration_settings;
- return &settings->uints.input_libretro_device[port];
- }
- unsigned input_config_get_device(unsigned port)
- {
- struct rarch_state *p_rarch = &rarch_st;
- settings_t *settings = p_rarch->configuration_settings;
- return settings->uints.input_libretro_device[port];
- }
- void input_config_set_device(unsigned port, unsigned id)
- {
- struct rarch_state *p_rarch = &rarch_st;
- settings_t *settings = p_rarch->configuration_settings;
- if (settings)
- configuration_set_uint(settings,
- settings->uints.input_libretro_device[port], id);
- }
- const struct retro_keybind *input_config_get_bind_auto(
- unsigned port, unsigned id)
- {
- struct rarch_state *p_rarch = &rarch_st;
- settings_t *settings = p_rarch->configuration_settings;
- unsigned joy_idx = settings->uints.input_joypad_map[port];
- if (joy_idx < MAX_USERS)
- return &input_autoconf_binds[joy_idx][id];
- return NULL;
- }
- void input_config_reset_autoconfig_binds(unsigned port)
- {
- unsigned i;
- if (port >= MAX_USERS)
- return;
- for (i = 0; i < RARCH_BIND_LIST_END; i++)
- {
- input_autoconf_binds[port][i].joykey = NO_BTN;
- input_autoconf_binds[port][i].joyaxis = AXIS_NONE;
- if (input_autoconf_binds[port][i].joykey_label)
- {
- free(input_autoconf_binds[port][i].joykey_label);
- input_autoconf_binds[port][i].joykey_label = NULL;
- }
- if (input_autoconf_binds[port][i].joyaxis_label)
- {
- free(input_autoconf_binds[port][i].joyaxis_label);
- input_autoconf_binds[port][i].joyaxis_label = NULL;
- }
- }
- }
- void input_config_reset(void)
- {
- unsigned i;
- struct rarch_state *p_rarch = &rarch_st;
- retro_assert(sizeof(input_config_binds[0]) >= sizeof(retro_keybinds_1));
- retro_assert(sizeof(input_config_binds[1]) >= sizeof(retro_keybinds_rest));
- memcpy(input_config_binds[0], retro_keybinds_1, sizeof(retro_keybinds_1));
- for (i = 1; i < MAX_USERS; i++)
- memcpy(input_config_binds[i], retro_keybinds_rest,
- sizeof(retro_keybinds_rest));
- for (i = 0; i < MAX_USERS; i++)
- {
- /* Note: Don't use input_config_clear_device_name()
- * here, since this will re-index devices each time
- * (not required - we are setting all 'name indices'
- * to zero manually) */
- p_rarch->input_device_info[i].name[0] = '\0';
- input_config_clear_device_display_name(i);
- input_config_clear_device_config_path(i);
- input_config_clear_device_config_name(i);
- input_config_clear_device_joypad_driver(i);
- input_config_set_device_vid(i, 0);
- input_config_set_device_pid(i, 0);
- input_config_set_device_autoconfigured(i, false);
- input_config_set_device_name_index(i, 0);
- input_config_reset_autoconfig_binds(i);
- p_rarch->libretro_input_binds[i] = input_config_binds[i];
- }
- }
- void config_read_keybinds_conf(void *data)
- {
- unsigned i;
- config_file_t *conf = (config_file_t*)data;
- if (!conf)
- return;
- for (i = 0; i < MAX_USERS; i++)
- {
- unsigned j;
- for (j = 0; input_config_bind_map_get_valid(j); j++)
- {
- struct retro_keybind *bind = &input_config_binds[i][j];
- const char *prefix = input_config_get_prefix(i, input_config_bind_map_get_meta(j));
- const char *btn = input_config_bind_map_get_base(j);
- if (!bind || !bind->valid)
- continue;
- if (!input_config_bind_map_get_valid(j))
- continue;
- if (!btn || !prefix)
- continue;
- input_config_parse_key(conf, prefix, btn, bind);
- input_config_parse_joy_button(conf, prefix, btn, bind);
- input_config_parse_joy_axis(conf, prefix, btn, bind);
- input_config_parse_mouse_button(conf, prefix, btn, bind);
- }
- }
- }
- void input_config_set_autoconfig_binds(unsigned port, void *data)
- {
- config_file_t *config = (config_file_t*)data;
- struct retro_keybind *binds = NULL;
- unsigned i;
- if ((port >= MAX_USERS) || !config)
- return;
- binds = input_autoconf_binds[port];
- for (i = 0; i < RARCH_BIND_LIST_END; i++)
- {
- input_config_parse_joy_button(config, "input",
- input_config_bind_map_get_base(i), &binds[i]);
- input_config_parse_joy_axis (config, "input",
- input_config_bind_map_get_base(i), &binds[i]);
- }
- }
- /**
- * input_config_save_keybinds_user:
- * @conf : pointer to config file object
- * @user : user number
- *
- * Save the current keybinds of a user (@user) to the config file (@conf).
- */
- void input_config_save_keybinds_user(void *data, unsigned user)
- {
- unsigned i = 0;
- config_file_t *conf = (config_file_t*)data;
- for (i = 0; input_config_bind_map_get_valid(i); i++)
- {
- char key[64];
- char btn[64];
- const char *prefix = input_config_get_prefix(user,
- input_config_bind_map_get_meta(i));
- const struct retro_keybind *bind = &input_config_binds[user][i];
- const char *base = input_config_bind_map_get_base(i);
- if (!prefix || !bind->valid)
- continue;
- key[0] = btn[0] = '\0';
- fill_pathname_join_delim(key, prefix, base, '_', sizeof(key));
- input_keymaps_translate_rk_to_str(bind->key, btn, sizeof(btn));
- config_set_string(conf, key, btn);
- input_config_save_keybind(conf, prefix, base, bind, true);
- }
- }
- static void save_keybind_hat(config_file_t *conf, const char *key,
- const struct retro_keybind *bind)
- {
- char config[16];
- unsigned hat = (unsigned)GET_HAT(bind->joykey);
- const char *dir = NULL;
- config[0] = '\0';
- switch (GET_HAT_DIR(bind->joykey))
- {
- case HAT_UP_MASK:
- dir = "up";
- break;
- case HAT_DOWN_MASK:
- dir = "down";
- break;
- case HAT_LEFT_MASK:
- dir = "left";
- break;
- case HAT_RIGHT_MASK:
- dir = "right";
- break;
- default:
- break;
- }
- snprintf(config, sizeof(config), "h%u%s", hat, dir);
- config_set_string(conf, key, config);
- }
- static void save_keybind_joykey(config_file_t *conf,
- const char *prefix,
- const char *base,
- const struct retro_keybind *bind, bool save_empty)
- {
- char key[64];
- key[0] = '\0';
- fill_pathname_join_delim_concat(key, prefix,
- base, '_', "_btn", sizeof(key));
- if (bind->joykey == NO_BTN)
- {
- if (save_empty)
- config_set_string(conf, key, "nul");
- }
- else if (GET_HAT_DIR(bind->joykey))
- save_keybind_hat(conf, key, bind);
- else
- config_set_uint64(conf, key, bind->joykey);
- }
- static void save_keybind_axis(config_file_t *conf,
- const char *prefix,
- const char *base,
- const struct retro_keybind *bind, bool save_empty)
- {
- char key[64];
- unsigned axis = 0;
- char dir = '\0';
- key[0] = '\0';
- fill_pathname_join_delim_concat(key,
- prefix, base, '_',
- "_axis",
- sizeof(key));
- if (bind->joyaxis == AXIS_NONE)
- {
- if (save_empty)
- config_set_string(conf, key, "nul");
- }
- else if (AXIS_NEG_GET(bind->joyaxis) != AXIS_DIR_NONE)
- {
- dir = '-';
- axis = AXIS_NEG_GET(bind->joyaxis);
- }
- else if (AXIS_POS_GET(bind->joyaxis) != AXIS_DIR_NONE)
- {
- dir = '+';
- axis = AXIS_POS_GET(bind->joyaxis);
- }
- if (dir)
- {
- char config[16];
- config[0] = '\0';
- snprintf(config, sizeof(config), "%c%u", dir, axis);
- config_set_string(conf, key, config);
- }
- }
- static void save_keybind_mbutton(config_file_t *conf,
- const char *prefix,
- const char *base,
- const struct retro_keybind *bind, bool save_empty)
- {
- char key[64];
- key[0] = '\0';
- fill_pathname_join_delim_concat(key, prefix,
- base, '_', "_mbtn", sizeof(key));
- switch (bind->mbutton)
- {
- case RETRO_DEVICE_ID_MOUSE_LEFT:
- config_set_uint64(conf, key, 1);
- break;
- case RETRO_DEVICE_ID_MOUSE_RIGHT:
- config_set_uint64(conf, key, 2);
- break;
- case RETRO_DEVICE_ID_MOUSE_MIDDLE:
- config_set_uint64(conf, key, 3);
- break;
- case RETRO_DEVICE_ID_MOUSE_BUTTON_4:
- config_set_uint64(conf, key, 4);
- break;
- case RETRO_DEVICE_ID_MOUSE_BUTTON_5:
- config_set_uint64(conf, key, 5);
- break;
- case RETRO_DEVICE_ID_MOUSE_WHEELUP:
- config_set_string(conf, key, "wu");
- break;
- case RETRO_DEVICE_ID_MOUSE_WHEELDOWN:
- config_set_string(conf, key, "wd");
- break;
- case RETRO_DEVICE_ID_MOUSE_HORIZ_WHEELUP:
- config_set_string(conf, key, "whu");
- break;
- case RETRO_DEVICE_ID_MOUSE_HORIZ_WHEELDOWN:
- config_set_string(conf, key, "whd");
- break;
- default:
- if (save_empty)
- config_set_string(conf, key, "nul");
- break;
- }
- }
- /**
- * input_config_save_keybind:
- * @conf : pointer to config file object
- * @prefix : prefix name of keybind
- * @base : base name of keybind
- * @bind : pointer to key binding object
- * @kb : save keyboard binds
- *
- * Save a key binding to the config file.
- */
- void input_config_save_keybind(void *data, const char *prefix,
- const char *base, const struct retro_keybind *bind,
- bool save_empty)
- {
- config_file_t *conf = (config_file_t*)data;
- save_keybind_joykey (conf, prefix, base, bind, save_empty);
- save_keybind_axis (conf, prefix, base, bind, save_empty);
- save_keybind_mbutton(conf, prefix, base, bind, save_empty);
- }
- /* MIDI */
- static midi_driver_t *midi_driver_find_driver(const char *ident)
- {
- unsigned i;
- for (i = 0; i < ARRAY_SIZE(midi_drivers); ++i)
- {
- if (string_is_equal(midi_drivers[i]->ident, ident))
- return midi_drivers[i];
- }
- RARCH_ERR("[MIDI]: Unknown driver \"%s\", falling back to \"null\" driver.\n", ident);
- return &midi_null;
- }
- static const void *midi_driver_find_handle(int index)
- {
- if (index < 0 || index >= ARRAY_SIZE(midi_drivers))
- return NULL;
- return midi_drivers[index];
- }
- struct string_list *midi_driver_get_avail_inputs(void)
- {
- struct rarch_state *p_rarch = &rarch_st;
- return p_rarch->midi_drv_inputs;
- }
- struct string_list *midi_driver_get_avail_outputs(void)
- {
- struct rarch_state *p_rarch = &rarch_st;
- return p_rarch->midi_drv_outputs;
- }
- static bool midi_driver_set_all_sounds_off(struct rarch_state *p_rarch)
- {
- midi_event_t event;
- uint8_t i;
- uint8_t data[3] = { 0xB0, 120, 0 };
- bool result = true;
- if (!p_rarch->midi_drv_data || !p_rarch->midi_drv_output_enabled)
- return false;
- event.data = data;
- event.data_size = sizeof(data);
- event.delta_time = 0;
- for (i = 0; i < 16; ++i)
- {
- data[0] = 0xB0 | i;
- if (!midi_drv->write(p_rarch->midi_drv_data, &event))
- result = false;
- }
- if (!midi_drv->flush(p_rarch->midi_drv_data))
- result = false;
- if (!result)
- RARCH_ERR("[MIDI]: All sounds off failed.\n");
- return result;
- }
- bool midi_driver_set_volume(unsigned volume)
- {
- midi_event_t event;
- struct rarch_state *p_rarch = &rarch_st;
- uint8_t data[8] = {
- 0xF0, 0x7F, 0x7F, 0x04, 0x01, 0, 0, 0xF7};
- if (!p_rarch->midi_drv_data || !p_rarch->midi_drv_output_enabled)
- return false;
- volume = (unsigned)(163.83 * volume + 0.5);
- if (volume > 16383)
- volume = 16383;
- data[5] = (uint8_t)(volume & 0x7F);
- data[6] = (uint8_t)(volume >> 7);
- event.data = data;
- event.data_size = sizeof(data);
- event.delta_time = 0;
- if (!midi_drv->write(p_rarch->midi_drv_data, &event))
- {
- RARCH_ERR("[MIDI]: Volume change failed.\n");
- return false;
- }
- return true;
- }
- static bool midi_driver_init_io_buffers(struct rarch_state *p_rarch)
- {
- uint8_t *midi_drv_input_buffer = (uint8_t*)malloc(MIDI_DRIVER_BUF_SIZE);
- uint8_t *midi_drv_output_buffer = (uint8_t*)malloc(MIDI_DRIVER_BUF_SIZE);
- if (!midi_drv_input_buffer || !midi_drv_output_buffer)
- {
- if (midi_drv_input_buffer)
- free(midi_drv_input_buffer);
- if (midi_drv_output_buffer)
- free(midi_drv_output_buffer);
- return false;
- }
- p_rarch->midi_drv_input_buffer = midi_drv_input_buffer;
- p_rarch->midi_drv_output_buffer = midi_drv_output_buffer;
- p_rarch->midi_drv_input_event.data = midi_drv_input_buffer;
- p_rarch->midi_drv_input_event.data_size = 0;
- p_rarch->midi_drv_output_event.data = midi_drv_output_buffer;
- p_rarch->midi_drv_output_event.data_size = 0;
- return true;
- }
- static void midi_driver_free(struct rarch_state *p_rarch)
- {
- if (p_rarch->midi_drv_data)
- {
- midi_drv->free(p_rarch->midi_drv_data);
- p_rarch->midi_drv_data = NULL;
- }
- if (p_rarch->midi_drv_inputs)
- {
- string_list_free(p_rarch->midi_drv_inputs);
- p_rarch->midi_drv_inputs = NULL;
- }
- if (p_rarch->midi_drv_outputs)
- {
- string_list_free(p_rarch->midi_drv_outputs);
- p_rarch->midi_drv_outputs = NULL;
- }
- if (p_rarch->midi_drv_input_buffer)
- {
- free(p_rarch->midi_drv_input_buffer);
- p_rarch->midi_drv_input_buffer = NULL;
- }
- if (p_rarch->midi_drv_output_buffer)
- {
- free(p_rarch->midi_drv_output_buffer);
- p_rarch->midi_drv_output_buffer = NULL;
- }
- p_rarch->midi_drv_input_enabled = false;
- p_rarch->midi_drv_output_enabled = false;
- }
- static bool midi_driver_init(struct rarch_state *p_rarch)
- {
- settings_t *settings = p_rarch->configuration_settings;
- union string_list_elem_attr attr = {0};
- const char *err_str = NULL;
- p_rarch->midi_drv_inputs = string_list_new();
- p_rarch->midi_drv_outputs = string_list_new();
- if (!settings)
- err_str = "settings unavailable";
- else if (!p_rarch->midi_drv_inputs || !p_rarch->midi_drv_outputs)
- err_str = "string_list_new failed";
- else if (!string_list_append(p_rarch->midi_drv_inputs, "Off", attr) ||
- !string_list_append(p_rarch->midi_drv_outputs, "Off", attr))
- err_str = "string_list_append failed";
- else
- {
- char * input = NULL;
- char * output = NULL;
- midi_drv = midi_driver_find_driver(
- settings->arrays.midi_driver);
- if (strcmp(midi_drv->ident, settings->arrays.midi_driver))
- {
- configuration_set_string(settings,
- settings->arrays.midi_driver, midi_drv->ident);
- }
- if (!midi_drv->get_avail_inputs(p_rarch->midi_drv_inputs))
- err_str = "list of input devices unavailable";
- else if (!midi_drv->get_avail_outputs(p_rarch->midi_drv_outputs))
- err_str = "list of output devices unavailable";
- else
- {
- if (string_is_not_equal(settings->arrays.midi_input, "Off"))
- {
- if (string_list_find_elem(p_rarch->midi_drv_inputs, settings->arrays.midi_input))
- input = settings->arrays.midi_input;
- else
- {
- RARCH_WARN("[MIDI]: Input device \"%s\" unavailable.\n",
- settings->arrays.midi_input);
- configuration_set_string(settings,
- settings->arrays.midi_input, "Off");
- }
- }
- if (string_is_not_equal(settings->arrays.midi_output, "Off"))
- {
- if (string_list_find_elem(p_rarch->midi_drv_outputs, settings->arrays.midi_output))
- output = settings->arrays.midi_output;
- else
- {
- RARCH_WARN("[MIDI]: Output device \"%s\" unavailable.\n",
- settings->arrays.midi_output);
- configuration_set_string(settings,
- settings->arrays.midi_output, "Off");
- }
- }
- p_rarch->midi_drv_data = midi_drv->init(input, output);
- if (!p_rarch->midi_drv_data)
- err_str = "driver init failed";
- else
- {
- p_rarch->midi_drv_input_enabled = (input != NULL);
- p_rarch->midi_drv_output_enabled = (output != NULL);
- if (!midi_driver_init_io_buffers(p_rarch))
- err_str = "out of memory";
- else
- {
- if (input)
- RARCH_LOG("[MIDI]: Input device \"%s\".\n", input);
- else
- RARCH_LOG("[MIDI]: Input disabled.\n");
- if (output)
- {
- RARCH_LOG("[MIDI]: Output device \"%s\".\n", output);
- midi_driver_set_volume(settings->uints.midi_volume);
- }
- else
- RARCH_LOG("[MIDI]: Output disabled.\n");
- }
- }
- }
- }
- if (err_str)
- {
- midi_driver_free(p_rarch);
- RARCH_ERR("[MIDI]: Initialization failed (%s).\n", err_str);
- }
- else
- RARCH_LOG("[MIDI]: Initialized \"%s\" driver.\n", midi_drv->ident);
- return err_str == NULL;
- }
- bool midi_driver_set_input(const char *input)
- {
- struct rarch_state *p_rarch = &rarch_st;
- if (!p_rarch->midi_drv_data)
- {
- #ifdef DEBUG
- RARCH_ERR("[MIDI]: midi_driver_set_input called on uninitialized driver.\n");
- #endif
- return false;
- }
- if (string_is_equal(input, "Off"))
- input = NULL;
- if (!midi_drv->set_input(p_rarch->midi_drv_data, input))
- {
- if (input)
- RARCH_ERR("[MIDI]: Failed to change input device to \"%s\".\n", input);
- else
- RARCH_ERR("[MIDI]: Failed to disable input.\n");
- return false;
- }
- if (input)
- RARCH_LOG("[MIDI]: Input device changed to \"%s\".\n", input);
- else
- RARCH_LOG("[MIDI]: Input disabled.\n");
- p_rarch->midi_drv_input_enabled = input != NULL;
- return true;
- }
- bool midi_driver_set_output(const char *output)
- {
- struct rarch_state *p_rarch = &rarch_st;
- if (!p_rarch->midi_drv_data)
- {
- #ifdef DEBUG
- RARCH_ERR("[MIDI]: midi_driver_set_output called on uninitialized driver.\n");
- #endif
- return false;
- }
- if (string_is_equal(output, "Off"))
- output = NULL;
- if (!midi_drv->set_output(p_rarch->midi_drv_data, output))
- {
- if (output)
- RARCH_ERR("[MIDI]: Failed to change output device to \"%s\".\n", output);
- else
- RARCH_ERR("[MIDI]: Failed to disable output.\n");
- return false;
- }
- if (output)
- {
- settings_t *settings = p_rarch->configuration_settings;
- p_rarch->midi_drv_output_enabled = true;
- RARCH_LOG("[MIDI]: Output device changed to \"%s\".\n", output);
- if (settings)
- midi_driver_set_volume(settings->uints.midi_volume);
- else
- RARCH_ERR("[MIDI]: Volume change failed (settings unavailable).\n");
- }
- else
- {
- p_rarch->midi_drv_output_enabled = false;
- RARCH_LOG("[MIDI]: Output disabled.\n");
- }
- return true;
- }
- static bool midi_driver_input_enabled(void)
- {
- struct rarch_state *p_rarch = &rarch_st;
- return p_rarch->midi_drv_input_enabled;
- }
- static bool midi_driver_output_enabled(void)
- {
- struct rarch_state *p_rarch = &rarch_st;
- return p_rarch->midi_drv_output_enabled;
- }
- static bool midi_driver_read(uint8_t *byte)
- {
- static int i;
- struct rarch_state *p_rarch = &rarch_st;
- if (!p_rarch->midi_drv_data || !p_rarch->midi_drv_input_enabled || !byte)
- {
- #ifdef DEBUG
- if (!p_rarch->midi_drv_data)
- RARCH_ERR("[MIDI]: midi_driver_read called on uninitialized driver.\n");
- else if (!p_rarch->midi_drv_input_enabled)
- RARCH_ERR("[MIDI]: midi_driver_read called when input is disabled.\n");
- else
- RARCH_ERR("[MIDI]: midi_driver_read called with null pointer.\n");
- #endif
- return false;
- }
- if (i == p_rarch->midi_drv_input_event.data_size)
- {
- p_rarch->midi_drv_input_event.data_size = MIDI_DRIVER_BUF_SIZE;
- if (!midi_drv->read(p_rarch->midi_drv_data,
- &p_rarch->midi_drv_input_event))
- {
- p_rarch->midi_drv_input_event.data_size = i;
- return false;
- }
- i = 0;
- #ifdef DEBUG
- if (p_rarch->midi_drv_input_event.data_size == 1)
- RARCH_LOG("[MIDI]: In [0x%02X].\n",
- p_rarch->midi_drv_input_event.data[0]);
- else if (p_rarch->midi_drv_input_event.data_size == 2)
- RARCH_LOG("[MIDI]: In [0x%02X, 0x%02X].\n",
- p_rarch->midi_drv_input_event.data[0],
- p_rarch->midi_drv_input_event.data[1]);
- else if (p_rarch->midi_drv_input_event.data_size == 3)
- RARCH_LOG("[MIDI]: In [0x%02X, 0x%02X, 0x%02X].\n",
- p_rarch->midi_drv_input_event.data[0],
- p_rarch->midi_drv_input_event.data[1],
- p_rarch->midi_drv_input_event.data[2]);
- else
- RARCH_LOG("[MIDI]: In [0x%02X, ...], size %u.\n",
- p_rarch->midi_drv_input_event.data[0],
- p_rarch->midi_drv_input_event.data_size);
- #endif
- }
- *byte = p_rarch->midi_drv_input_event.data[i++];
- return true;
- }
- static bool midi_driver_write(uint8_t byte, uint32_t delta_time)
- {
- static int event_size;
- struct rarch_state *p_rarch = &rarch_st;
- if (!p_rarch->midi_drv_data || !p_rarch->midi_drv_output_enabled)
- {
- #ifdef DEBUG
- if (!p_rarch->midi_drv_data)
- RARCH_ERR("[MIDI]: midi_driver_write called on uninitialized driver.\n");
- else
- RARCH_ERR("[MIDI]: midi_driver_write called when output is disabled.\n");
- #endif
- return false;
- }
- if (byte >= 0x80)
- {
- if (p_rarch->midi_drv_output_event.data_size &&
- p_rarch->midi_drv_output_event.data[0] == 0xF0)
- {
- if (byte == 0xF7)
- event_size = (int)p_rarch->midi_drv_output_event.data_size + 1;
- else
- {
- if (!midi_drv->write(p_rarch->midi_drv_data,
- &p_rarch->midi_drv_output_event))
- return false;
- #ifdef DEBUG
- switch (p_rarch->midi_drv_output_event.data_size)
- {
- case 1:
- RARCH_LOG("[MIDI]: Out [0x%02X].\n",
- p_rarch->midi_drv_output_event.data[0]);
- break;
- case 2:
- RARCH_LOG("[MIDI]: Out [0x%02X, 0x%02X].\n",
- p_rarch->midi_drv_output_event.data[0],
- p_rarch->midi_drv_output_event.data[1]);
- break;
- case 3:
- RARCH_LOG("[MIDI]: Out [0x%02X, 0x%02X, 0x%02X].\n",
- p_rarch->midi_drv_output_event.data[0],
- p_rarch->midi_drv_output_event.data[1],
- p_rarch->midi_drv_output_event.data[2]);
- break;
- default:
- RARCH_LOG("[MIDI]: Out [0x%02X, ...], size %u.\n",
- p_rarch->midi_drv_output_event.data[0],
- p_rarch->midi_drv_output_event.data_size);
- break;
- }
- #endif
- p_rarch->midi_drv_output_pending = true;
- event_size = (int)midi_driver_get_event_size(byte);
- p_rarch->midi_drv_output_event.data_size = 0;
- p_rarch->midi_drv_output_event.delta_time = 0;
- }
- }
- else
- {
- event_size = (int)midi_driver_get_event_size(byte);
- p_rarch->midi_drv_output_event.data_size = 0;
- p_rarch->midi_drv_output_event.delta_time = 0;
- }
- }
- if (p_rarch->midi_drv_output_event.data_size < MIDI_DRIVER_BUF_SIZE)
- {
- p_rarch->midi_drv_output_event.data[p_rarch->midi_drv_output_event.data_size] = byte;
- ++p_rarch->midi_drv_output_event.data_size;
- p_rarch->midi_drv_output_event.delta_time += delta_time;
- }
- else
- {
- #ifdef DEBUG
- RARCH_ERR("[MIDI]: Output event dropped.\n");
- #endif
- return false;
- }
- if (p_rarch->midi_drv_output_event.data_size == event_size)
- {
- if (!midi_drv->write(p_rarch->midi_drv_data,
- &p_rarch->midi_drv_output_event))
- return false;
- #ifdef DEBUG
- switch (p_rarch->midi_drv_output_event.data_size)
- {
- case 1:
- RARCH_LOG("[MIDI]: Out [0x%02X].\n",
- p_rarch->midi_drv_output_event.data[0]);
- break;
- case 2:
- RARCH_LOG("[MIDI]: Out [0x%02X, 0x%02X].\n",
- p_rarch->midi_drv_output_event.data[0],
- p_rarch->midi_drv_output_event.data[1]);
- break;
- case 3:
- RARCH_LOG("[MIDI]: Out [0x%02X, 0x%02X, 0x%02X].\n",
- p_rarch->midi_drv_output_event.data[0],
- p_rarch->midi_drv_output_event.data[1],
- p_rarch->midi_drv_output_event.data[2]);
- break;
- default:
- RARCH_LOG("[MIDI]: Out [0x%02X, ...], size %u.\n",
- p_rarch->midi_drv_output_event.data[0],
- p_rarch->midi_drv_output_event.data_size);
- break;
- }
- #endif
- p_rarch->midi_drv_output_pending = true;
- p_rarch->midi_drv_output_event.data_size = 0;
- p_rarch->midi_drv_output_event.delta_time = 0;
- }
- return true;
- }
- static bool midi_driver_flush(void)
- {
- struct rarch_state *p_rarch = &rarch_st;
- if (!p_rarch->midi_drv_data)
- return false;
- if (p_rarch->midi_drv_output_pending)
- p_rarch->midi_drv_output_pending =
- !midi_drv->flush(p_rarch->midi_drv_data);
- return !p_rarch->midi_drv_output_pending;
- }
- size_t midi_driver_get_event_size(uint8_t status)
- {
- static const uint8_t midi_drv_ev_sizes[128] =
- {
- 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
- 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
- 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
- 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
- 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
- 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
- 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
- 0, 2, 3, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
- };
- if (status < 0x80)
- {
- #ifdef DEBUG
- RARCH_ERR("[MIDI]: midi_driver_get_event_size called with invalid status.\n");
- #endif
- return 0;
- }
- return midi_drv_ev_sizes[status - 0x80];
- }
- /* AUDIO */
- static enum resampler_quality audio_driver_get_resampler_quality(
- struct rarch_state *p_rarch)
- {
- settings_t *settings = p_rarch->configuration_settings;
- if (!settings)
- return RESAMPLER_QUALITY_DONTCARE;
- return (enum resampler_quality)settings->uints.audio_resampler_quality;
- }
- #ifdef HAVE_AUDIOMIXER
- audio_mixer_stream_t *audio_driver_mixer_get_stream(unsigned i)
- {
- struct rarch_state *p_rarch = &rarch_st;
- if (i > (AUDIO_MIXER_MAX_SYSTEM_STREAMS-1))
- return NULL;
- return &p_rarch->audio_mixer_streams[i];
- }
- const char *audio_driver_mixer_get_stream_name(unsigned i)
- {
- struct rarch_state *p_rarch = &rarch_st;
- if (i > (AUDIO_MIXER_MAX_SYSTEM_STREAMS-1))
- return msg_hash_to_str(MENU_ENUM_LABEL_VALUE_NOT_AVAILABLE);
- if (!string_is_empty(p_rarch->audio_mixer_streams[i].name))
- return p_rarch->audio_mixer_streams[i].name;
- return msg_hash_to_str(MENU_ENUM_LABEL_VALUE_NOT_AVAILABLE);
- }
- static void audio_driver_mixer_deinit(struct rarch_state *p_rarch)
- {
- unsigned i;
- p_rarch->audio_mixer_active = false;
- for (i = 0; i < AUDIO_MIXER_MAX_SYSTEM_STREAMS; i++)
- {
- audio_driver_mixer_stop_stream(i);
- audio_driver_mixer_remove_stream(i);
- }
- audio_mixer_done();
- }
- #endif
- /**
- * audio_compute_buffer_statistics:
- *
- * Computes audio buffer statistics.
- *
- **/
- static bool audio_compute_buffer_statistics(
- struct rarch_state *p_rarch,
- audio_statistics_t *stats)
- {
- unsigned i, low_water_size, high_water_size, avg, stddev;
- uint64_t accum = 0;
- uint64_t accum_var = 0;
- unsigned low_water_count = 0;
- unsigned high_water_count = 0;
- unsigned samples = MIN(
- (unsigned)p_rarch->audio_driver_free_samples_count,
- AUDIO_BUFFER_FREE_SAMPLES_COUNT);
- if (samples < 3)
- return false;
- stats->samples = (unsigned)
- p_rarch->audio_driver_free_samples_count;
- #ifdef WARPUP
- /* uint64 to double not implemented, fair chance
- * signed int64 to double doesn't exist either */
- /* https://forums.libretro.com/t/unsupported-platform-help/13903/ */
- (void)stddev;
- #elif defined(_MSC_VER) && _MSC_VER <= 1200
- /* FIXME: error C2520: conversion from unsigned __int64
- * to double not implemented, use signed __int64 */
- (void)stddev;
- #else
- for (i = 1; i < samples; i++)
- accum += p_rarch->audio_driver_free_samples_buf[i];
- avg = (unsigned)accum / (samples - 1);
- for (i = 1; i < samples; i++)
- {
- int diff = avg - p_rarch->audio_driver_free_samples_buf[i];
- accum_var += diff * diff;
- }
- stddev = (unsigned)
- sqrt((double)accum_var / (samples - 2));
- stats->average_buffer_saturation = (1.0f - (float)avg
- / p_rarch->audio_driver_buffer_size) * 100.0;
- stats->std_deviation_percentage = ((float)stddev
- / p_rarch->audio_driver_buffer_size) * 100.0;
- #endif
- low_water_size = (unsigned)(p_rarch->audio_driver_buffer_size * 3 / 4);
- high_water_size = (unsigned)(p_rarch->audio_driver_buffer_size / 4);
- for (i = 1; i < samples; i++)
- {
- if (p_rarch->audio_driver_free_samples_buf[i] >= low_water_size)
- low_water_count++;
- else if (p_rarch->audio_driver_free_samples_buf[i] <= high_water_size)
- high_water_count++;
- }
- stats->close_to_underrun = (100.0f * low_water_count) / (samples - 1);
- stats->close_to_blocking = (100.0f * high_water_count) / (samples - 1);
- return true;
- }
- #ifdef DEBUG
- static void report_audio_buffer_statistics(struct rarch_state *p_rarch)
- {
- audio_statistics_t audio_stats;
- audio_stats.samples = 0;
- audio_stats.average_buffer_saturation = 0.0f;
- audio_stats.std_deviation_percentage = 0.0f;
- audio_stats.close_to_underrun = 0.0f;
- audio_stats.close_to_blocking = 0.0f;
- if (!audio_compute_buffer_statistics(p_rarch, &audio_stats))
- return;
- RARCH_LOG("[Audio]: Average audio buffer saturation: %.2f %%,"
- " standard deviation (percentage points): %.2f %%.\n"
- "[Audio]: Amount of time spent close to underrun: %.2f %%."
- " Close to blocking: %.2f %%.\n",
- audio_stats.average_buffer_saturation,
- audio_stats.std_deviation_percentage,
- audio_stats.close_to_underrun,
- audio_stats.close_to_blocking);
- }
- #endif
- /**
- * config_get_audio_driver_options:
- *
- * Get an enumerated list of all audio driver names, separated by '|'.
- *
- * Returns: string listing of all audio driver names, separated by '|'.
- **/
- const char *config_get_audio_driver_options(void)
- {
- return char_list_new_special(STRING_LIST_AUDIO_DRIVERS, NULL);
- }
- static void audio_driver_deinit_resampler(struct rarch_state *p_rarch)
- {
- if (p_rarch->audio_driver_resampler && p_rarch->audio_driver_resampler_data)
- p_rarch->audio_driver_resampler->free(p_rarch->audio_driver_resampler_data);
- p_rarch->audio_driver_resampler = NULL;
- p_rarch->audio_driver_resampler_data = NULL;
- }
- static bool audio_driver_deinit_internal(struct rarch_state *p_rarch)
- {
- settings_t *settings = p_rarch->configuration_settings;
- bool audio_enable = settings->bools.audio_enable;
- if (p_rarch->current_audio && p_rarch->current_audio->free)
- {
- if (p_rarch->audio_driver_context_audio_data)
- p_rarch->current_audio->free(
- p_rarch->audio_driver_context_audio_data);
- p_rarch->audio_driver_context_audio_data = NULL;
- }
- if (p_rarch->audio_driver_output_samples_conv_buf)
- memalign_free(p_rarch->audio_driver_output_samples_conv_buf);
- p_rarch->audio_driver_output_samples_conv_buf = NULL;
- if (p_rarch->audio_driver_input_data)
- memalign_free(p_rarch->audio_driver_input_data);
- p_rarch->audio_driver_input_data = NULL;
- p_rarch->audio_driver_data_ptr = 0;
- #ifdef HAVE_REWIND
- if (p_rarch->audio_driver_rewind_buf)
- memalign_free(p_rarch->audio_driver_rewind_buf);
- p_rarch->audio_driver_rewind_buf = NULL;
- p_rarch->audio_driver_rewind_size = 0;
- #endif
- if (!audio_enable)
- {
- p_rarch->audio_driver_active = false;
- return false;
- }
- audio_driver_deinit_resampler(p_rarch);
- if (p_rarch->audio_driver_output_samples_buf)
- memalign_free(p_rarch->audio_driver_output_samples_buf);
- p_rarch->audio_driver_output_samples_buf = NULL;
- #ifdef HAVE_DSP_FILTER
- audio_driver_dsp_filter_free();
- #endif
- #ifdef DEBUG
- report_audio_buffer_statistics(p_rarch);
- #endif
- return true;
- }
- static bool audio_driver_free_devices_list(struct rarch_state *p_rarch)
- {
- if (!p_rarch->current_audio || !p_rarch->current_audio->device_list_free
- || !p_rarch->audio_driver_context_audio_data)
- return false;
- p_rarch->current_audio->device_list_free(
- p_rarch->audio_driver_context_audio_data,
- p_rarch->audio_driver_devices_list);
- p_rarch->audio_driver_devices_list = NULL;
- return true;
- }
- static bool audio_driver_deinit(struct rarch_state *p_rarch)
- {
- #ifdef HAVE_AUDIOMIXER
- audio_driver_mixer_deinit(p_rarch);
- #endif
- audio_driver_free_devices_list(p_rarch);
- return audio_driver_deinit_internal(p_rarch);
- }
- static bool audio_driver_find_driver(struct rarch_state *p_rarch)
- {
- settings_t *settings = p_rarch->configuration_settings;
- int i = (int)driver_find_index(
- "audio_driver",
- settings->arrays.audio_driver);
- if (i >= 0)
- p_rarch->current_audio = (const audio_driver_t*)
- audio_drivers[i];
- else
- {
- if (verbosity_is_enabled())
- {
- unsigned d;
- RARCH_ERR("Couldn't find any audio driver named \"%s\"\n",
- settings->arrays.audio_driver);
- RARCH_LOG_OUTPUT("Available audio drivers are:\n");
- for (d = 0; audio_drivers[d]; d++)
- {
- if (audio_drivers[d])
- RARCH_LOG_OUTPUT("\t%s\n", audio_drivers[d]->ident);
- }
- RARCH_WARN("Going to default to first audio driver...\n");
- }
- p_rarch->current_audio = (const audio_driver_t*)
- audio_drivers[0];
- if (!p_rarch->current_audio)
- retroarch_fail(1, "audio_driver_find()");
- }
- return true;
- }
- static bool audio_driver_init_internal(
- struct rarch_state *p_rarch,
- bool audio_cb_inited)
- {
- unsigned new_rate = 0;
- float *samples_buf = NULL;
- size_t max_bufsamples = AUDIO_CHUNK_SIZE_NONBLOCKING * 2;
- settings_t *settings = p_rarch->configuration_settings;
- bool audio_enable = settings->bools.audio_enable;
- bool audio_sync = settings->bools.audio_sync;
- bool audio_rate_control = settings->bools.audio_rate_control;
- float slowmotion_ratio = settings->floats.slowmotion_ratio;
- unsigned audio_latency = (p_rarch->runloop_audio_latency > settings->uints.audio_latency) ?
- p_rarch->runloop_audio_latency : settings->uints.audio_latency;
- #ifdef HAVE_REWIND
- int16_t *rewind_buf = NULL;
- #endif
- /* Accomodate rewind since at some point we might have two full buffers. */
- size_t outsamples_max = AUDIO_CHUNK_SIZE_NONBLOCKING * 2 * AUDIO_MAX_RATIO * slowmotion_ratio;
- int16_t *conv_buf = (int16_t*)memalign_alloc(64, outsamples_max * sizeof(int16_t));
- float *audio_buf = (float*)memalign_alloc(64, AUDIO_CHUNK_SIZE_NONBLOCKING * 2 * sizeof(float));
- convert_s16_to_float_init_simd();
- convert_float_to_s16_init_simd();
- /* Used for recording even if audio isn't enabled. */
- retro_assert(conv_buf != NULL);
- retro_assert(audio_buf != NULL);
- if (!conv_buf || !audio_buf)
- goto error;
- memset(audio_buf, 0, AUDIO_CHUNK_SIZE_NONBLOCKING * 2 * sizeof(float));
- p_rarch->audio_driver_input_data = audio_buf;
- p_rarch->audio_driver_output_samples_conv_buf = conv_buf;
- p_rarch->audio_driver_chunk_block_size = AUDIO_CHUNK_SIZE_BLOCKING;
- p_rarch->audio_driver_chunk_nonblock_size = AUDIO_CHUNK_SIZE_NONBLOCKING;
- p_rarch->audio_driver_chunk_size = p_rarch->audio_driver_chunk_block_size;
- #ifdef HAVE_REWIND
- /* Needs to be able to hold full content of a full max_bufsamples
- * in addition to its own. */
- rewind_buf = (int16_t*)memalign_alloc(64, max_bufsamples * sizeof(int16_t));
- retro_assert(rewind_buf != NULL);
- if (!rewind_buf)
- goto error;
- p_rarch->audio_driver_rewind_buf = rewind_buf;
- p_rarch->audio_driver_rewind_size = max_bufsamples;
- #endif
- if (!audio_enable)
- {
- p_rarch->audio_driver_active = false;
- return false;
- }
- audio_driver_find_driver(p_rarch);
- if (!p_rarch->current_audio || !p_rarch->current_audio->init)
- {
- RARCH_ERR("Failed to initialize audio driver. Will continue without audio.\n");
- p_rarch->audio_driver_active = false;
- return false;
- }
- #ifdef HAVE_THREADS
- if (audio_cb_inited)
- {
- RARCH_LOG("[Audio]: Starting threaded audio driver ...\n");
- if (!audio_init_thread(
- &p_rarch->current_audio,
- &p_rarch->audio_driver_context_audio_data,
- *settings->arrays.audio_device
- ? settings->arrays.audio_device : NULL,
- settings->uints.audio_out_rate, &new_rate,
- audio_latency,
- settings->uints.audio_block_frames,
- p_rarch->current_audio))
- {
- RARCH_ERR("Cannot open threaded audio driver ... Exiting ...\n");
- retroarch_fail(1, "audio_driver_init_internal()");
- }
- }
- else
- #endif
- {
- p_rarch->audio_driver_context_audio_data =
- p_rarch->current_audio->init(*settings->arrays.audio_device ?
- settings->arrays.audio_device : NULL,
- settings->uints.audio_out_rate,
- audio_latency,
- settings->uints.audio_block_frames,
- &new_rate);
- }
- if (new_rate != 0)
- configuration_set_int(settings, settings->uints.audio_out_rate, new_rate);
- if (!p_rarch->audio_driver_context_audio_data)
- {
- RARCH_ERR("Failed to initialize audio driver. Will continue without audio.\n");
- p_rarch->audio_driver_active = false;
- }
- p_rarch->audio_driver_use_float = false;
- if ( p_rarch->audio_driver_active
- && p_rarch->current_audio->use_float(
- p_rarch->audio_driver_context_audio_data))
- p_rarch->audio_driver_use_float = true;
- if (!audio_sync && p_rarch->audio_driver_active)
- {
- if (p_rarch->audio_driver_active &&
- p_rarch->audio_driver_context_audio_data)
- p_rarch->current_audio->set_nonblock_state(
- p_rarch->audio_driver_context_audio_data, true);
- p_rarch->audio_driver_chunk_size =
- p_rarch->audio_driver_chunk_nonblock_size;
- }
- if (p_rarch->audio_driver_input <= 0.0f)
- {
- /* Should never happen. */
- RARCH_WARN("[Audio]: Input rate is invalid (%.3f Hz)."
- " Using output rate (%u Hz).\n",
- p_rarch->audio_driver_input, settings->uints.audio_out_rate);
- p_rarch->audio_driver_input = settings->uints.audio_out_rate;
- }
- p_rarch->audio_source_ratio_original =
- p_rarch->audio_source_ratio_current =
- (double)settings->uints.audio_out_rate / p_rarch->audio_driver_input;
- if (!retro_resampler_realloc(
- &p_rarch->audio_driver_resampler_data,
- &p_rarch->audio_driver_resampler,
- settings->arrays.audio_resampler,
- audio_driver_get_resampler_quality(p_rarch),
- p_rarch->audio_source_ratio_original))
- {
- RARCH_ERR("Failed to initialize resampler \"%s\".\n",
- settings->arrays.audio_resampler);
- p_rarch->audio_driver_active = false;
- }
- p_rarch->audio_driver_data_ptr = 0;
- retro_assert(settings->uints.audio_out_rate <
- p_rarch->audio_driver_input * AUDIO_MAX_RATIO);
- samples_buf = (float*)memalign_alloc(64, outsamples_max * sizeof(float));
- retro_assert(samples_buf != NULL);
- if (!samples_buf)
- goto error;
- p_rarch->audio_driver_output_samples_buf = (float*)samples_buf;
- p_rarch->audio_driver_control = false;
- if (
- !audio_cb_inited
- && p_rarch->audio_driver_active
- && audio_rate_control
- )
- {
- /* Audio rate control requires write_avail
- * and buffer_size to be implemented. */
- if (p_rarch->current_audio->buffer_size)
- {
- p_rarch->audio_driver_buffer_size =
- p_rarch->current_audio->buffer_size(
- p_rarch->audio_driver_context_audio_data);
- p_rarch->audio_driver_control = true;
- }
- else
- RARCH_WARN("[Audio]: Rate control was desired, but driver does not support needed features.\n");
- }
- command_event(CMD_EVENT_DSP_FILTER_INIT, NULL);
- p_rarch->audio_driver_free_samples_count = 0;
- #ifdef HAVE_AUDIOMIXER
- audio_mixer_init(settings->uints.audio_out_rate);
- #endif
- /* Threaded driver is initially stopped. */
- if (
- p_rarch->audio_driver_active
- && audio_cb_inited
- )
- audio_driver_start(p_rarch,
- false);
- return true;
- error:
- return audio_driver_deinit(p_rarch);
- }
- /**
- * audio_driver_flush:
- * @data : pointer to audio buffer.
- * @right : amount of samples to write.
- *
- * Writes audio samples to audio driver. Will first
- * perform DSP processing (if enabled) and resampling.
- **/
- static void audio_driver_flush(
- struct rarch_state *p_rarch,
- float slowmotion_ratio,
- bool audio_fastforward_mute,
- const int16_t *data, size_t samples,
- bool is_slowmotion, bool is_fastmotion)
- {
- struct resampler_data src_data;
- float audio_volume_gain = (p_rarch->audio_driver_mute_enable ||
- (audio_fastforward_mute && is_fastmotion)) ?
- 0.0f : p_rarch->audio_driver_volume_gain;
- src_data.data_out = NULL;
- src_data.output_frames = 0;
- convert_s16_to_float(p_rarch->audio_driver_input_data, data, samples,
- audio_volume_gain);
- src_data.data_in = p_rarch->audio_driver_input_data;
- src_data.input_frames = samples >> 1;
- #ifdef HAVE_DSP_FILTER
- if (p_rarch->audio_driver_dsp)
- {
- struct retro_dsp_data dsp_data;
- dsp_data.input = NULL;
- dsp_data.input_frames = 0;
- dsp_data.output = NULL;
- dsp_data.output_frames = 0;
- dsp_data.input = p_rarch->audio_driver_input_data;
- dsp_data.input_frames = (unsigned)(samples >> 1);
- retro_dsp_filter_process(p_rarch->audio_driver_dsp, &dsp_data);
- if (dsp_data.output)
- {
- src_data.data_in = dsp_data.output;
- src_data.input_frames = dsp_data.output_frames;
- }
- }
- #endif
- src_data.data_out = p_rarch->audio_driver_output_samples_buf;
- if (p_rarch->audio_driver_control)
- {
- /* Readjust the audio input rate. */
- int half_size =
- (int)(p_rarch->audio_driver_buffer_size / 2);
- int avail =
- (int)p_rarch->current_audio->write_avail(
- p_rarch->audio_driver_context_audio_data);
- int delta_mid = avail - half_size;
- double direction = (double)delta_mid / half_size;
- double adjust = 1.0 +
- p_rarch->audio_driver_rate_control_delta * direction;
- unsigned write_idx =
- p_rarch->audio_driver_free_samples_count++ &
- (AUDIO_BUFFER_FREE_SAMPLES_COUNT - 1);
- p_rarch->audio_driver_free_samples_buf
- [write_idx] = avail;
- p_rarch->audio_source_ratio_current =
- p_rarch->audio_source_ratio_original * adjust;
- #if 0
- if (verbosity_is_enabled())
- {
- RARCH_LOG_OUTPUT("[Audio]: Audio buffer is %u%% full\n",
- (unsigned)(100 - (avail * 100) /
- p_rarch->audio_driver_buffer_size));
- RARCH_LOG_OUTPUT("[Audio]: New rate: %lf, Orig rate: %lf\n",
- p_rarch->audio_source_ratio_current,
- p_rarch->audio_source_ratio_original);
- }
- #endif
- }
- src_data.ratio = p_rarch->audio_source_ratio_current;
- if (is_slowmotion)
- src_data.ratio *= slowmotion_ratio;
- /* Note: Ideally we would divide by the user-configured
- * 'fastforward_ratio' when fast forward is enabled,
- * but in practice this doesn't work:
- * - 'fastforward_ratio' is only a limit. If the host
- * cannot push frames fast enough, the actual ratio
- * will be lower - and crackling will ensue
- * - Most of the time 'fastforward_ratio' will be
- * zero (unlimited)
- * So what we would need to do is measure the time since
- * the last audio flush operation, and calculate a 'real'
- * fast-forward ratio - but this doesn't work either.
- * The measurement is inaccurate and the frame-by-frame
- * fluctuations are too large, so crackling is unavoidable.
- * Since it's going to crackle anyway, there's no point
- * trying to do anything. Just leave the ratio as-is,
- * and hope for the best... */
- p_rarch->audio_driver_resampler->process(
- p_rarch->audio_driver_resampler_data, &src_data);
- #ifdef HAVE_AUDIOMIXER
- if (p_rarch->audio_mixer_active)
- {
- bool override = true;
- float mixer_gain = 0.0f;
- bool audio_driver_mixer_mute_enable =
- p_rarch->audio_driver_mixer_mute_enable;
- if (!audio_driver_mixer_mute_enable)
- {
- if (p_rarch->audio_driver_mixer_volume_gain == 1.0f)
- override = false;
- mixer_gain =
- p_rarch->audio_driver_mixer_volume_gain;
- }
- audio_mixer_mix(
- p_rarch->audio_driver_output_samples_buf,
- src_data.output_frames, mixer_gain, override);
- }
- #endif
- {
- const void *output_data = p_rarch->audio_driver_output_samples_buf;
- unsigned output_frames = (unsigned)src_data.output_frames;
- if (p_rarch->audio_driver_use_float)
- output_frames *= sizeof(float);
- else
- {
- convert_float_to_s16(p_rarch->audio_driver_output_samples_conv_buf,
- (const float*)output_data, output_frames * 2);
- output_data = p_rarch->audio_driver_output_samples_conv_buf;
- output_frames *= sizeof(int16_t);
- }
- if (p_rarch->current_audio->write(
- p_rarch->audio_driver_context_audio_data,
- output_data, output_frames * 2) < 0)
- p_rarch->audio_driver_active = false;
- }
- }
- /**
- * audio_driver_sample:
- * @left : value of the left audio channel.
- * @right : value of the right audio channel.
- *
- * Audio sample render callback function.
- **/
- static void audio_driver_sample(int16_t left, int16_t right)
- {
- struct rarch_state *p_rarch = &rarch_st;
- if (p_rarch->audio_suspended)
- return;
- p_rarch->audio_driver_output_samples_conv_buf[p_rarch->audio_driver_data_ptr++] = left;
- p_rarch->audio_driver_output_samples_conv_buf[p_rarch->audio_driver_data_ptr++] = right;
- if (p_rarch->audio_driver_data_ptr < p_rarch->audio_driver_chunk_size)
- return;
- if ( p_rarch->recording_data &&
- p_rarch->recording_driver &&
- p_rarch->recording_driver->push_audio)
- {
- struct record_audio_data ffemu_data;
- ffemu_data.data = p_rarch->audio_driver_output_samples_conv_buf;
- ffemu_data.frames = p_rarch->audio_driver_data_ptr / 2;
- p_rarch->recording_driver->push_audio(p_rarch->recording_data, &ffemu_data);
- }
- if (!(p_rarch->runloop_paused ||
- !p_rarch->audio_driver_active ||
- !p_rarch->audio_driver_output_samples_buf))
- audio_driver_flush(
- p_rarch,
- p_rarch->configuration_settings->floats.slowmotion_ratio,
- p_rarch->configuration_settings->bools.audio_fastforward_mute,
- p_rarch->audio_driver_output_samples_conv_buf,
- p_rarch->audio_driver_data_ptr,
- p_rarch->runloop_slowmotion,
- p_rarch->runloop_fastmotion);
- p_rarch->audio_driver_data_ptr = 0;
- }
- #ifdef HAVE_MENU
- static void audio_driver_menu_sample(void)
- {
- static int16_t samples_buf[1024] = {0};
- struct rarch_state *p_rarch = &rarch_st;
- struct retro_system_av_info *av_info = &p_rarch->video_driver_av_info;
- const struct retro_system_timing *info =
- (const struct retro_system_timing*)&av_info->timing;
- unsigned sample_count = (info->sample_rate / info->fps) * 2;
- bool check_flush = !(
- p_rarch->runloop_paused ||
- !p_rarch->audio_driver_active ||
- !p_rarch->audio_driver_output_samples_buf);
- while (sample_count > 1024)
- {
- if ( p_rarch->recording_data &&
- p_rarch->recording_driver &&
- p_rarch->recording_driver->push_audio)
- {
- struct record_audio_data ffemu_data;
- ffemu_data.data = samples_buf;
- ffemu_data.frames = 1024 / 2;
- p_rarch->recording_driver->push_audio(
- p_rarch->recording_data, &ffemu_data);
- }
- if (check_flush)
- audio_driver_flush(
- p_rarch,
- p_rarch->configuration_settings->floats.slowmotion_ratio,
- p_rarch->configuration_settings->bools.audio_fastforward_mute,
- samples_buf,
- 1024,
- p_rarch->runloop_slowmotion,
- p_rarch->runloop_fastmotion);
- sample_count -= 1024;
- }
- if ( p_rarch->recording_data &&
- p_rarch->recording_driver &&
- p_rarch->recording_driver->push_audio)
- {
- struct record_audio_data ffemu_data;
- ffemu_data.data = samples_buf;
- ffemu_data.frames = sample_count / 2;
- p_rarch->recording_driver->push_audio(
- p_rarch->recording_data, &ffemu_data);
- }
- if (check_flush)
- audio_driver_flush(
- p_rarch,
- p_rarch->configuration_settings->floats.slowmotion_ratio,
- p_rarch->configuration_settings->bools.audio_fastforward_mute,
- samples_buf,
- sample_count,
- p_rarch->runloop_slowmotion,
- p_rarch->runloop_fastmotion);
- }
- #endif
- /**
- * audio_driver_sample_batch:
- * @data : pointer to audio buffer.
- * @frames : amount of audio frames to push.
- *
- * Batched audio sample render callback function.
- *
- * Returns: amount of frames sampled. Will be equal to @frames
- * unless @frames exceeds (AUDIO_CHUNK_SIZE_NONBLOCKING / 2).
- **/
- static size_t audio_driver_sample_batch(const int16_t *data, size_t frames)
- {
- struct rarch_state *p_rarch = &rarch_st;
- if (frames > (AUDIO_CHUNK_SIZE_NONBLOCKING >> 1))
- frames = AUDIO_CHUNK_SIZE_NONBLOCKING >> 1;
- if (p_rarch->audio_suspended)
- return frames;
- if ( p_rarch->recording_data &&
- p_rarch->recording_driver &&
- p_rarch->recording_driver->push_audio)
- {
- struct record_audio_data ffemu_data;
- ffemu_data.data = data;
- ffemu_data.frames = (frames << 1) / 2;
- p_rarch->recording_driver->push_audio(
- p_rarch->recording_data, &ffemu_data);
- }
- if (!(
- p_rarch->runloop_paused ||
- !p_rarch->audio_driver_active ||
- !p_rarch->audio_driver_output_samples_buf))
- audio_driver_flush(
- p_rarch,
- p_rarch->configuration_settings->floats.slowmotion_ratio,
- p_rarch->configuration_settings->bools.audio_fastforward_mute,
- data,
- frames << 1,
- p_rarch->runloop_slowmotion,
- p_rarch->runloop_fastmotion);
- return frames;
- }
- #ifdef HAVE_REWIND
- /**
- * audio_driver_sample_rewind:
- * @left : value of the left audio channel.
- * @right : value of the right audio channel.
- *
- * Audio sample render callback function (rewind version).
- * This callback function will be used instead of
- * audio_driver_sample when rewinding is activated.
- **/
- static void audio_driver_sample_rewind(int16_t left, int16_t right)
- {
- struct rarch_state *p_rarch = &rarch_st;
- if (p_rarch->audio_driver_rewind_ptr == 0)
- return;
- p_rarch->audio_driver_rewind_buf[--p_rarch->audio_driver_rewind_ptr] = right;
- p_rarch->audio_driver_rewind_buf[--p_rarch->audio_driver_rewind_ptr] = left;
- }
- /**
- * audio_driver_sample_batch_rewind:
- * @data : pointer to audio buffer.
- * @frames : amount of audio frames to push.
- *
- * Batched audio sample render callback function (rewind version).
- *
- * This callback function will be used instead of
- * audio_driver_sample_batch when rewinding is activated.
- *
- * Returns: amount of frames sampled. Will be equal to @frames
- * unless @frames exceeds (AUDIO_CHUNK_SIZE_NONBLOCKING / 2).
- **/
- static size_t audio_driver_sample_batch_rewind(
- const int16_t *data, size_t frames)
- {
- size_t i;
- struct rarch_state *p_rarch = &rarch_st;
- size_t samples = frames << 1;
- for (i = 0; i < samples; i++)
- {
- if (p_rarch->audio_driver_rewind_ptr > 0)
- p_rarch->audio_driver_rewind_buf[--p_rarch->audio_driver_rewind_ptr] = data[i];
- }
- return frames;
- }
- #endif
- #ifdef HAVE_DSP_FILTER
- void audio_driver_dsp_filter_free(void)
- {
- struct rarch_state *p_rarch = &rarch_st;
- if (p_rarch->audio_driver_dsp)
- retro_dsp_filter_free(p_rarch->audio_driver_dsp);
- p_rarch->audio_driver_dsp = NULL;
- }
- bool audio_driver_dsp_filter_init(const char *device)
- {
- retro_dsp_filter_t *audio_driver_dsp = NULL;
- struct rarch_state *p_rarch = &rarch_st;
- struct string_list *plugs = NULL;
- #if defined(HAVE_DYLIB) && !defined(HAVE_FILTERS_BUILTIN)
- char basedir[PATH_MAX_LENGTH];
- char ext_name[PATH_MAX_LENGTH];
- basedir[0] = ext_name[0] = '\0';
- fill_pathname_basedir(basedir, device, sizeof(basedir));
- if (!frontend_driver_get_core_extension(ext_name, sizeof(ext_name)))
- return false;
- plugs = dir_list_new(basedir, ext_name, false, true, false, false);
- if (!plugs)
- return false;
- #endif
- audio_driver_dsp = retro_dsp_filter_new(
- device, plugs, p_rarch->audio_driver_input);
- if (!audio_driver_dsp)
- return false;
- p_rarch->audio_driver_dsp = audio_driver_dsp;
- return true;
- }
- #endif
- void audio_driver_set_buffer_size(size_t bufsize)
- {
- struct rarch_state *p_rarch = &rarch_st;
- p_rarch->audio_driver_buffer_size = bufsize;
- }
- static float audio_driver_monitor_adjust_system_rates(
- settings_t *settings,
- struct retro_system_av_info *av_info)
- {
- bool vrr_runloop_enable = settings->bools.vrr_runloop_enable;
- const float target_video_sync_rate =
- settings->floats.video_refresh_rate / settings->uints.video_swap_interval;
- float max_timing_skew = settings->floats.audio_max_timing_skew;
- const struct retro_system_timing *info =
- (const struct retro_system_timing*)&av_info->timing;
- float timing_skew =
- fabs(1.0f - info->fps / target_video_sync_rate);
- float ret = info->sample_rate;
- if (timing_skew <= max_timing_skew && !vrr_runloop_enable)
- ret *= target_video_sync_rate / info->fps;
- return ret;
- }
- #ifdef HAVE_REWIND
- void audio_driver_setup_rewind(void)
- {
- unsigned i;
- struct rarch_state *p_rarch = &rarch_st;
- /* Push audio ready to be played. */
- p_rarch->audio_driver_rewind_ptr = p_rarch->audio_driver_rewind_size;
- for (i = 0; i < p_rarch->audio_driver_data_ptr; i += 2)
- {
- if (p_rarch->audio_driver_rewind_ptr > 0)
- p_rarch->audio_driver_rewind_buf[
- --p_rarch->audio_driver_rewind_ptr] =
- p_rarch->audio_driver_output_samples_conv_buf[i + 1];
- if (p_rarch->audio_driver_rewind_ptr > 0)
- p_rarch->audio_driver_rewind_buf[--p_rarch->audio_driver_rewind_ptr] =
- p_rarch->audio_driver_output_samples_conv_buf[i + 0];
- }
- p_rarch->audio_driver_data_ptr = 0;
- }
- #endif
- bool audio_driver_get_devices_list(void **data)
- {
- struct rarch_state *p_rarch = &rarch_st;
- struct string_list**ptr = (struct string_list**)data;
- if (!ptr)
- return false;
- *ptr = p_rarch->audio_driver_devices_list;
- return true;
- }
- #ifdef HAVE_AUDIOMIXER
- bool audio_driver_mixer_extension_supported(const char *ext)
- {
- unsigned i;
- struct string_list str_list;
- union string_list_elem_attr attr;
- bool ret = false;
- attr.i = 0;
- if (!string_list_initialize(&str_list))
- return false;
- #ifdef HAVE_STB_VORBIS
- string_list_append(&str_list, "ogg", attr);
- #endif
- #ifdef HAVE_IBXM
- string_list_append(&str_list, "mod", attr);
- string_list_append(&str_list, "s3m", attr);
- string_list_append(&str_list, "xm", attr);
- #endif
- #ifdef HAVE_DR_FLAC
- string_list_append(&str_list, "flac", attr);
- #endif
- #ifdef HAVE_DR_MP3
- string_list_append(&str_list, "mp3", attr);
- #endif
- string_list_append(&str_list, "wav", attr);
- for (i = 0; i < str_list.size; i++)
- {
- const char *str_ext = str_list.elems[i].data;
- if (string_is_equal_noncase(str_ext, ext))
- {
- ret = true;
- break;
- }
- }
- string_list_deinitialize(&str_list);
- return ret;
- }
- static int audio_mixer_find_index(
- struct rarch_state *p_rarch,
- audio_mixer_sound_t *sound)
- {
- unsigned i;
- for (i = 0; i < AUDIO_MIXER_MAX_SYSTEM_STREAMS; i++)
- {
- audio_mixer_sound_t *handle = p_rarch->audio_mixer_streams[i].handle;
- if (handle == sound)
- return i;
- }
- return -1;
- }
- static void audio_mixer_play_stop_cb(
- audio_mixer_sound_t *sound, unsigned reason)
- {
- struct rarch_state *p_rarch = &rarch_st;
- int idx = audio_mixer_find_index(p_rarch, sound);
- switch (reason)
- {
- case AUDIO_MIXER_SOUND_FINISHED:
- audio_mixer_destroy(sound);
- if (idx >= 0)
- {
- unsigned i = (unsigned)idx;
- if (!string_is_empty(p_rarch->audio_mixer_streams[i].name))
- free(p_rarch->audio_mixer_streams[i].name);
- p_rarch->audio_mixer_streams[i].name = NULL;
- p_rarch->audio_mixer_streams[i].state = AUDIO_STREAM_STATE_NONE;
- p_rarch->audio_mixer_streams[i].volume = 0.0f;
- p_rarch->audio_mixer_streams[i].buf = NULL;
- p_rarch->audio_mixer_streams[i].stop_cb = NULL;
- p_rarch->audio_mixer_streams[i].handle = NULL;
- p_rarch->audio_mixer_streams[i].voice = NULL;
- }
- break;
- case AUDIO_MIXER_SOUND_STOPPED:
- break;
- case AUDIO_MIXER_SOUND_REPEATED:
- break;
- }
- }
- static void audio_mixer_menu_stop_cb(
- audio_mixer_sound_t *sound, unsigned reason)
- {
- struct rarch_state *p_rarch = &rarch_st;
- int idx = audio_mixer_find_index(p_rarch, sound);
- switch (reason)
- {
- case AUDIO_MIXER_SOUND_FINISHED:
- if (idx >= 0)
- {
- unsigned i = (unsigned)idx;
- p_rarch->audio_mixer_streams[i].state = AUDIO_STREAM_STATE_STOPPED;
- p_rarch->audio_mixer_streams[i].volume = 0.0f;
- }
- break;
- case AUDIO_MIXER_SOUND_STOPPED:
- break;
- case AUDIO_MIXER_SOUND_REPEATED:
- break;
- }
- }
- static void audio_mixer_play_stop_sequential_cb(
- audio_mixer_sound_t *sound, unsigned reason)
- {
- struct rarch_state *p_rarch = &rarch_st;
- int idx = audio_mixer_find_index(p_rarch, sound);
- switch (reason)
- {
- case AUDIO_MIXER_SOUND_FINISHED:
- audio_mixer_destroy(sound);
- if (idx >= 0)
- {
- unsigned i = (unsigned)idx;
- if (!string_is_empty(p_rarch->audio_mixer_streams[i].name))
- free(p_rarch->audio_mixer_streams[i].name);
- if (i < AUDIO_MIXER_MAX_STREAMS)
- p_rarch->audio_mixer_streams[i].stream_type = AUDIO_STREAM_TYPE_USER;
- else
- p_rarch->audio_mixer_streams[i].stream_type = AUDIO_STREAM_TYPE_SYSTEM;
- p_rarch->audio_mixer_streams[i].name = NULL;
- p_rarch->audio_mixer_streams[i].state = AUDIO_STREAM_STATE_NONE;
- p_rarch->audio_mixer_streams[i].volume = 0.0f;
- p_rarch->audio_mixer_streams[i].buf = NULL;
- p_rarch->audio_mixer_streams[i].stop_cb = NULL;
- p_rarch->audio_mixer_streams[i].handle = NULL;
- p_rarch->audio_mixer_streams[i].voice = NULL;
- i++;
- for (; i < AUDIO_MIXER_MAX_SYSTEM_STREAMS; i++)
- {
- if (p_rarch->audio_mixer_streams[i].state
- == AUDIO_STREAM_STATE_STOPPED)
- {
- audio_driver_mixer_play_stream_sequential(i);
- break;
- }
- }
- }
- break;
- case AUDIO_MIXER_SOUND_STOPPED:
- break;
- case AUDIO_MIXER_SOUND_REPEATED:
- break;
- }
- }
- static bool audio_driver_mixer_get_free_stream_slot(
- unsigned *id, enum audio_mixer_stream_type type)
- {
- unsigned i = AUDIO_MIXER_MAX_STREAMS;
- unsigned count = AUDIO_MIXER_MAX_SYSTEM_STREAMS;
- struct rarch_state *p_rarch = &rarch_st;
- if (type == AUDIO_STREAM_TYPE_USER)
- {
- i = 0;
- count = AUDIO_MIXER_MAX_STREAMS;
- }
- for (; i < count; i++)
- {
- if (p_rarch->audio_mixer_streams[i].state == AUDIO_STREAM_STATE_NONE)
- {
- *id = i;
- return true;
- }
- }
- return false;
- }
- bool audio_driver_mixer_add_stream(audio_mixer_stream_params_t *params)
- {
- struct rarch_state *p_rarch = &rarch_st;
- unsigned free_slot = 0;
- audio_mixer_voice_t *voice = NULL;
- audio_mixer_sound_t *handle = NULL;
- audio_mixer_stop_cb_t stop_cb = audio_mixer_play_stop_cb;
- bool looped = false;
- void *buf = NULL;
- if (params->stream_type == AUDIO_STREAM_TYPE_NONE)
- return false;
- switch (params->slot_selection_type)
- {
- case AUDIO_MIXER_SLOT_SELECTION_MANUAL:
- free_slot = params->slot_selection_idx;
- break;
- case AUDIO_MIXER_SLOT_SELECTION_AUTOMATIC:
- default:
- if (!audio_driver_mixer_get_free_stream_slot(
- &free_slot, params->stream_type))
- return false;
- break;
- }
- if (params->state == AUDIO_STREAM_STATE_NONE)
- return false;
- buf = malloc(params->bufsize);
- if (!buf)
- return false;
- memcpy(buf, params->buf, params->bufsize);
- switch (params->type)
- {
- case AUDIO_MIXER_TYPE_WAV:
- handle = audio_mixer_load_wav(buf, (int32_t)params->bufsize);
- /* WAV is a special case - input buffer is not
- * free()'d when sound playback is complete (it is
- * converted to a PCM buffer, which is free()'d instead),
- * so have to do it here */
- free(buf);
- buf = NULL;
- break;
- case AUDIO_MIXER_TYPE_OGG:
- handle = audio_mixer_load_ogg(buf, (int32_t)params->bufsize);
- break;
- case AUDIO_MIXER_TYPE_MOD:
- handle = audio_mixer_load_mod(buf, (int32_t)params->bufsize);
- break;
- case AUDIO_MIXER_TYPE_FLAC:
- #ifdef HAVE_DR_FLAC
- handle = audio_mixer_load_flac(buf, (int32_t)params->bufsize);
- #endif
- break;
- case AUDIO_MIXER_TYPE_MP3:
- #ifdef HAVE_DR_MP3
- handle = audio_mixer_load_mp3(buf, (int32_t)params->bufsize);
- #endif
- break;
- case AUDIO_MIXER_TYPE_NONE:
- break;
- }
- if (!handle)
- {
- free(buf);
- return false;
- }
- switch (params->state)
- {
- case AUDIO_STREAM_STATE_PLAYING_LOOPED:
- looped = true;
- voice = audio_mixer_play(handle, looped, params->volume, stop_cb);
- break;
- case AUDIO_STREAM_STATE_PLAYING:
- voice = audio_mixer_play(handle, looped, params->volume, stop_cb);
- break;
- case AUDIO_STREAM_STATE_PLAYING_SEQUENTIAL:
- stop_cb = audio_mixer_play_stop_sequential_cb;
- voice = audio_mixer_play(handle, looped, params->volume, stop_cb);
- break;
- default:
- break;
- }
- p_rarch->audio_mixer_active = true;
- p_rarch->audio_mixer_streams[free_slot].name =
- !string_is_empty(params->basename) ? strdup(params->basename) : NULL;
- p_rarch->audio_mixer_streams[free_slot].buf = buf;
- p_rarch->audio_mixer_streams[free_slot].handle = handle;
- p_rarch->audio_mixer_streams[free_slot].voice = voice;
- p_rarch->audio_mixer_streams[free_slot].stream_type = params->stream_type;
- p_rarch->audio_mixer_streams[free_slot].type = params->type;
- p_rarch->audio_mixer_streams[free_slot].state = params->state;
- p_rarch->audio_mixer_streams[free_slot].volume = params->volume;
- p_rarch->audio_mixer_streams[free_slot].stop_cb = stop_cb;
- return true;
- }
- enum audio_mixer_state audio_driver_mixer_get_stream_state(unsigned i)
- {
- struct rarch_state *p_rarch = &rarch_st;
- if (i >= AUDIO_MIXER_MAX_SYSTEM_STREAMS)
- return AUDIO_STREAM_STATE_NONE;
- return p_rarch->audio_mixer_streams[i].state;
- }
- static void audio_driver_mixer_play_stream_internal(
- struct rarch_state *p_rarch,
- unsigned i, unsigned type)
- {
- if (i >= AUDIO_MIXER_MAX_SYSTEM_STREAMS)
- return;
- switch (p_rarch->audio_mixer_streams[i].state)
- {
- case AUDIO_STREAM_STATE_STOPPED:
- p_rarch->audio_mixer_streams[i].voice =
- audio_mixer_play(p_rarch->audio_mixer_streams[i].handle,
- (type == AUDIO_STREAM_STATE_PLAYING_LOOPED) ? true : false,
- 1.0f, p_rarch->audio_mixer_streams[i].stop_cb);
- p_rarch->audio_mixer_streams[i].state = (enum audio_mixer_state)type;
- break;
- case AUDIO_STREAM_STATE_PLAYING:
- case AUDIO_STREAM_STATE_PLAYING_LOOPED:
- case AUDIO_STREAM_STATE_PLAYING_SEQUENTIAL:
- case AUDIO_STREAM_STATE_NONE:
- break;
- }
- }
- static void audio_driver_load_menu_bgm_callback(retro_task_t *task,
- void *task_data, void *user_data, const char *error)
- {
- bool contentless = false;
- bool is_inited = false;
- content_get_status(&contentless, &is_inited);
- if (!is_inited)
- audio_driver_mixer_play_menu_sound_looped(AUDIO_MIXER_SYSTEM_SLOT_BGM);
- }
- void audio_driver_load_system_sounds(void)
- {
- char sounds_path[PATH_MAX_LENGTH];
- char sounds_fallback_path[PATH_MAX_LENGTH];
- char basename_noext[PATH_MAX_LENGTH];
- struct rarch_state *p_rarch = &rarch_st;
- settings_t *settings = p_rarch->configuration_settings;
- const char *dir_assets = settings->paths.directory_assets;
- const bool audio_enable_menu = settings->bools.audio_enable_menu;
- const bool audio_enable_menu_ok = audio_enable_menu && settings->bools.audio_enable_menu_ok;
- const bool audio_enable_menu_cancel = audio_enable_menu && settings->bools.audio_enable_menu_cancel;
- const bool audio_enable_menu_notice = audio_enable_menu && settings->bools.audio_enable_menu_notice;
- const bool audio_enable_menu_bgm = audio_enable_menu && settings->bools.audio_enable_menu_bgm;
- const bool audio_enable_cheevo_unlock = settings->bools.cheevos_unlock_sound_enable;
- const char *path_ok = NULL;
- const char *path_cancel = NULL;
- const char *path_notice = NULL;
- const char *path_bgm = NULL;
- const char *path_cheevo_unlock = NULL;
- struct string_list *list = NULL;
- struct string_list *list_fallback = NULL;
- unsigned i = 0;
- if (!audio_enable_menu && !audio_enable_cheevo_unlock)
- goto end;
- sounds_path[0] = sounds_fallback_path[0] =
- basename_noext[0] ='\0';
- fill_pathname_join(
- sounds_fallback_path,
- dir_assets,
- "sounds",
- sizeof(sounds_fallback_path));
- fill_pathname_application_special(
- sounds_path,
- sizeof(sounds_path),
- APPLICATION_SPECIAL_DIRECTORY_ASSETS_SOUNDS);
- list = dir_list_new(sounds_path, MENU_SOUND_FORMATS, false, false, false, false);
- list_fallback = dir_list_new(sounds_fallback_path, MENU_SOUND_FORMATS, false, false, false, false);
- if (!list)
- {
- list = list_fallback;
- list_fallback = NULL;
- }
- if (!list || list->size == 0)
- goto end;
- if (list_fallback && list_fallback->size > 0)
- {
- for (i = 0; i < list_fallback->size; i++)
- {
- if (list->size == 0 || !string_list_find_elem(list, list_fallback->elems[i].data))
- {
- union string_list_elem_attr attr = {0};
- string_list_append(list, list_fallback->elems[i].data, attr);
- }
- }
- }
- for (i = 0; i < list->size; i++)
- {
- const char *path = list->elems[i].data;
- const char *ext = path_get_extension(path);
- if (audio_driver_mixer_extension_supported(ext))
- {
- basename_noext[0] = '\0';
- fill_pathname_base_noext(basename_noext, path, sizeof(basename_noext));
- if (string_is_equal_noncase(basename_noext, "ok"))
- path_ok = path;
- else if (string_is_equal_noncase(basename_noext, "cancel"))
- path_cancel = path;
- else if (string_is_equal_noncase(basename_noext, "notice"))
- path_notice = path;
- else if (string_is_equal_noncase(basename_noext, "bgm"))
- path_bgm = path;
- else if (string_is_equal_noncase(basename_noext, "unlock"))
- path_cheevo_unlock = path;
- }
- }
- if (path_ok && audio_enable_menu_ok)
- task_push_audio_mixer_load(path_ok, NULL, NULL, true, AUDIO_MIXER_SLOT_SELECTION_MANUAL, AUDIO_MIXER_SYSTEM_SLOT_OK);
- if (path_cancel && audio_enable_menu_cancel)
- task_push_audio_mixer_load(path_cancel, NULL, NULL, true, AUDIO_MIXER_SLOT_SELECTION_MANUAL, AUDIO_MIXER_SYSTEM_SLOT_CANCEL);
- if (path_notice && audio_enable_menu_notice)
- task_push_audio_mixer_load(path_notice, NULL, NULL, true, AUDIO_MIXER_SLOT_SELECTION_MANUAL, AUDIO_MIXER_SYSTEM_SLOT_NOTICE);
- if (path_bgm && audio_enable_menu_bgm)
- task_push_audio_mixer_load(path_bgm, audio_driver_load_menu_bgm_callback, NULL, true, AUDIO_MIXER_SLOT_SELECTION_MANUAL, AUDIO_MIXER_SYSTEM_SLOT_BGM);
- if (path_cheevo_unlock && audio_enable_cheevo_unlock)
- task_push_audio_mixer_load(path_cheevo_unlock, NULL, NULL, true, AUDIO_MIXER_SLOT_SELECTION_MANUAL, AUDIO_MIXER_SYSTEM_SLOT_ACHIEVEMENT_UNLOCK);
- end:
- if (list)
- string_list_free(list);
- if (list_fallback)
- string_list_free(list_fallback);
- }
- void audio_driver_mixer_play_stream(unsigned i)
- {
- struct rarch_state *p_rarch = &rarch_st;
- p_rarch->audio_mixer_streams[i].stop_cb = audio_mixer_play_stop_cb;
- audio_driver_mixer_play_stream_internal(p_rarch,
- i, AUDIO_STREAM_STATE_PLAYING);
- }
- void audio_driver_mixer_play_menu_sound_looped(unsigned i)
- {
- struct rarch_state *p_rarch = &rarch_st;
- p_rarch->audio_mixer_streams[i].stop_cb = audio_mixer_menu_stop_cb;
- audio_driver_mixer_play_stream_internal(p_rarch,
- i, AUDIO_STREAM_STATE_PLAYING_LOOPED);
- }
- void audio_driver_mixer_play_menu_sound(unsigned i)
- {
- struct rarch_state *p_rarch = &rarch_st;
- p_rarch->audio_mixer_streams[i].stop_cb = audio_mixer_menu_stop_cb;
- audio_driver_mixer_play_stream_internal(p_rarch,
- i, AUDIO_STREAM_STATE_PLAYING);
- }
- void audio_driver_mixer_play_stream_looped(unsigned i)
- {
- struct rarch_state *p_rarch = &rarch_st;
- p_rarch->audio_mixer_streams[i].stop_cb = audio_mixer_play_stop_cb;
- audio_driver_mixer_play_stream_internal(p_rarch,
- i, AUDIO_STREAM_STATE_PLAYING_LOOPED);
- }
- void audio_driver_mixer_play_stream_sequential(unsigned i)
- {
- struct rarch_state *p_rarch = &rarch_st;
- p_rarch->audio_mixer_streams[i].stop_cb = audio_mixer_play_stop_sequential_cb;
- audio_driver_mixer_play_stream_internal(p_rarch,
- i, AUDIO_STREAM_STATE_PLAYING_SEQUENTIAL);
- }
- float audio_driver_mixer_get_stream_volume(unsigned i)
- {
- struct rarch_state *p_rarch = &rarch_st;
- if (i >= AUDIO_MIXER_MAX_SYSTEM_STREAMS)
- return 0.0f;
- return p_rarch->audio_mixer_streams[i].volume;
- }
- void audio_driver_mixer_set_stream_volume(unsigned i, float vol)
- {
- audio_mixer_voice_t *voice = NULL;
- struct rarch_state *p_rarch = &rarch_st;
- if (i >= AUDIO_MIXER_MAX_SYSTEM_STREAMS)
- return;
- p_rarch->audio_mixer_streams[i].volume = vol;
- voice =
- p_rarch->audio_mixer_streams[i].voice;
- if (voice)
- audio_mixer_voice_set_volume(voice, DB_TO_GAIN(vol));
- }
- void audio_driver_mixer_stop_stream(unsigned i)
- {
- bool set_state = false;
- struct rarch_state *p_rarch = &rarch_st;
- if (i >= AUDIO_MIXER_MAX_SYSTEM_STREAMS)
- return;
- switch (p_rarch->audio_mixer_streams[i].state)
- {
- case AUDIO_STREAM_STATE_PLAYING:
- case AUDIO_STREAM_STATE_PLAYING_LOOPED:
- case AUDIO_STREAM_STATE_PLAYING_SEQUENTIAL:
- set_state = true;
- break;
- case AUDIO_STREAM_STATE_STOPPED:
- case AUDIO_STREAM_STATE_NONE:
- break;
- }
- if (set_state)
- {
- audio_mixer_voice_t *voice = p_rarch->audio_mixer_streams[i].voice;
- if (voice)
- audio_mixer_stop(voice);
- p_rarch->audio_mixer_streams[i].state = AUDIO_STREAM_STATE_STOPPED;
- p_rarch->audio_mixer_streams[i].volume = 1.0f;
- }
- }
- void audio_driver_mixer_remove_stream(unsigned i)
- {
- bool destroy = false;
- struct rarch_state *p_rarch = &rarch_st;
- if (i >= AUDIO_MIXER_MAX_SYSTEM_STREAMS)
- return;
- switch (p_rarch->audio_mixer_streams[i].state)
- {
- case AUDIO_STREAM_STATE_PLAYING:
- case AUDIO_STREAM_STATE_PLAYING_LOOPED:
- case AUDIO_STREAM_STATE_PLAYING_SEQUENTIAL:
- audio_driver_mixer_stop_stream(i);
- destroy = true;
- break;
- case AUDIO_STREAM_STATE_STOPPED:
- destroy = true;
- break;
- case AUDIO_STREAM_STATE_NONE:
- break;
- }
- if (destroy)
- {
- audio_mixer_sound_t *handle = p_rarch->audio_mixer_streams[i].handle;
- if (handle)
- audio_mixer_destroy(handle);
- if (!string_is_empty(p_rarch->audio_mixer_streams[i].name))
- free(p_rarch->audio_mixer_streams[i].name);
- p_rarch->audio_mixer_streams[i].state = AUDIO_STREAM_STATE_NONE;
- p_rarch->audio_mixer_streams[i].stop_cb = NULL;
- p_rarch->audio_mixer_streams[i].volume = 0.0f;
- p_rarch->audio_mixer_streams[i].handle = NULL;
- p_rarch->audio_mixer_streams[i].voice = NULL;
- p_rarch->audio_mixer_streams[i].name = NULL;
- }
- }
- #endif
- bool audio_driver_enable_callback(void)
- {
- struct rarch_state *p_rarch = &rarch_st;
- if (!p_rarch->audio_callback.callback)
- return false;
- if (p_rarch->audio_callback.set_state)
- p_rarch->audio_callback.set_state(true);
- return true;
- }
- bool audio_driver_disable_callback(void)
- {
- struct rarch_state *p_rarch = &rarch_st;
- if (!p_rarch->audio_callback.callback)
- return false;
- if (p_rarch->audio_callback.set_state)
- p_rarch->audio_callback.set_state(false);
- return true;
- }
- bool audio_driver_callback(void)
- {
- struct rarch_state *p_rarch = &rarch_st;
- settings_t *settings = p_rarch->configuration_settings;
- bool core_paused = p_rarch->runloop_paused || (settings->bools.menu_pause_libretro && p_rarch->menu_driver_alive);
- if (!p_rarch->audio_callback.callback)
- return false;
- if (!core_paused && p_rarch->audio_callback.callback)
- p_rarch->audio_callback.callback();
- return true;
- }
- bool audio_driver_has_callback(void)
- {
- struct rarch_state *p_rarch = &rarch_st;
- if (p_rarch->audio_callback.callback)
- return true;
- return false;
- }
- #ifdef HAVE_AUDIOMIXER
- bool audio_driver_mixer_toggle_mute(void)
- {
- struct rarch_state *p_rarch = &rarch_st;
- p_rarch->audio_driver_mixer_mute_enable =
- !p_rarch->audio_driver_mixer_mute_enable;
- return true;
- }
- #endif
- static INLINE bool audio_driver_alive(struct rarch_state *p_rarch)
- {
- if ( p_rarch->current_audio
- && p_rarch->current_audio->alive
- && p_rarch->audio_driver_context_audio_data)
- return p_rarch->current_audio->alive(p_rarch->audio_driver_context_audio_data);
- return false;
- }
- static bool audio_driver_start(struct rarch_state *p_rarch,
- bool is_shutdown)
- {
- if (!p_rarch->current_audio || !p_rarch->current_audio->start
- || !p_rarch->audio_driver_context_audio_data)
- goto error;
- if (!p_rarch->current_audio->start(
- p_rarch->audio_driver_context_audio_data, is_shutdown))
- goto error;
- return true;
- error:
- RARCH_ERR("%s\n",
- msg_hash_to_str(MSG_FAILED_TO_START_AUDIO_DRIVER));
- p_rarch->audio_driver_active = false;
- return false;
- }
- static bool audio_driver_stop(struct rarch_state *p_rarch)
- {
- if ( !p_rarch->current_audio
- || !p_rarch->current_audio->stop
- || !p_rarch->audio_driver_context_audio_data
- || !audio_driver_alive(p_rarch)
- )
- return false;
- return p_rarch->current_audio->stop(
- p_rarch->audio_driver_context_audio_data);
- }
- #ifdef HAVE_REWIND
- void audio_driver_frame_is_reverse(void)
- {
- struct rarch_state *p_rarch = &rarch_st;
- /* We just rewound. Flush rewind audio buffer. */
- if ( p_rarch->recording_data &&
- p_rarch->recording_driver &&
- p_rarch->recording_driver->push_audio)
- {
- struct record_audio_data ffemu_data;
- ffemu_data.data = p_rarch->audio_driver_rewind_buf +
- p_rarch->audio_driver_rewind_ptr;
- ffemu_data.frames = (p_rarch->audio_driver_rewind_size -
- p_rarch->audio_driver_rewind_ptr) / 2;
- p_rarch->recording_driver->push_audio(p_rarch->recording_data, &ffemu_data);
- }
- if (!(
- p_rarch->runloop_paused ||
- !p_rarch->audio_driver_active ||
- !p_rarch->audio_driver_output_samples_buf))
- audio_driver_flush(
- p_rarch,
- p_rarch->configuration_settings->floats.slowmotion_ratio,
- p_rarch->configuration_settings->bools.audio_fastforward_mute,
- p_rarch->audio_driver_rewind_buf +
- p_rarch->audio_driver_rewind_ptr,
- p_rarch->audio_driver_rewind_size -
- p_rarch->audio_driver_rewind_ptr,
- p_rarch->runloop_slowmotion,
- p_rarch->runloop_fastmotion);
- }
- #endif
- void audio_set_float(enum audio_action action, float val)
- {
- struct rarch_state *p_rarch = &rarch_st;
- switch (action)
- {
- case AUDIO_ACTION_VOLUME_GAIN:
- p_rarch->audio_driver_volume_gain = DB_TO_GAIN(val);
- break;
- case AUDIO_ACTION_MIXER_VOLUME_GAIN:
- #ifdef HAVE_AUDIOMIXER
- p_rarch->audio_driver_mixer_volume_gain = DB_TO_GAIN(val);
- #endif
- break;
- case AUDIO_ACTION_RATE_CONTROL_DELTA:
- p_rarch->audio_driver_rate_control_delta = val;
- break;
- case AUDIO_ACTION_NONE:
- default:
- break;
- }
- }
- float *audio_get_float_ptr(enum audio_action action)
- {
- struct rarch_state *p_rarch = &rarch_st;
- switch (action)
- {
- case AUDIO_ACTION_RATE_CONTROL_DELTA:
- return &p_rarch->audio_driver_rate_control_delta;
- case AUDIO_ACTION_NONE:
- default:
- break;
- }
- return NULL;
- }
- bool *audio_get_bool_ptr(enum audio_action action)
- {
- struct rarch_state *p_rarch = &rarch_st;
- switch (action)
- {
- case AUDIO_ACTION_MIXER_MUTE_ENABLE:
- #ifdef HAVE_AUDIOMIXER
- return &p_rarch->audio_driver_mixer_mute_enable;
- #else
- break;
- #endif
- case AUDIO_ACTION_MUTE_ENABLE:
- return &p_rarch->audio_driver_mute_enable;
- case AUDIO_ACTION_NONE:
- default:
- break;
- }
- return NULL;
- }
- /* VIDEO */
- const char *video_display_server_get_ident(void)
- {
- if (!current_display_server)
- return FILE_PATH_UNKNOWN;
- return current_display_server->ident;
- }
- void* video_display_server_init(enum rarch_display_type type)
- {
- struct rarch_state *p_rarch = &rarch_st;
- video_display_server_destroy();
- switch (type)
- {
- case RARCH_DISPLAY_WIN32:
- #if defined(_WIN32) && !defined(_XBOX) && !defined(__WINRT__)
- current_display_server = &dispserv_win32;
- #endif
- break;
- case RARCH_DISPLAY_X11:
- #if defined(HAVE_X11)
- current_display_server = &dispserv_x11;
- #endif
- break;
- default:
- #if defined(ANDROID)
- current_display_server = &dispserv_android;
- #else
- current_display_server = &dispserv_null;
- #endif
- break;
- }
- if (current_display_server)
- {
- if (current_display_server->init)
- p_rarch->current_display_server_data = current_display_server->init();
- if (!string_is_empty(current_display_server->ident))
- {
- RARCH_LOG("[Video]: Found display server: %s\n",
- current_display_server->ident);
- }
- }
- p_rarch->initial_screen_orientation =
- video_display_server_get_screen_orientation();
- p_rarch->current_screen_orientation =
- p_rarch->initial_screen_orientation;
- return p_rarch->current_display_server_data;
- }
- void video_display_server_destroy(void)
- {
- struct rarch_state *p_rarch = &rarch_st;
- const enum rotation initial_screen_orientation = p_rarch->initial_screen_orientation;
- const enum rotation current_screen_orientation = p_rarch->current_screen_orientation;
- if (initial_screen_orientation != current_screen_orientation)
- video_display_server_set_screen_orientation(initial_screen_orientation);
- if (current_display_server)
- if (p_rarch->current_display_server_data)
- current_display_server->destroy(p_rarch->current_display_server_data);
- }
- bool video_display_server_set_window_opacity(unsigned opacity)
- {
- struct rarch_state *p_rarch = &rarch_st;
- if (current_display_server && current_display_server->set_window_opacity)
- return current_display_server->set_window_opacity(
- p_rarch->current_display_server_data, opacity);
- return false;
- }
- bool video_display_server_set_window_progress(int progress, bool finished)
- {
- struct rarch_state *p_rarch = &rarch_st;
- if (current_display_server && current_display_server->set_window_progress)
- return current_display_server->set_window_progress(
- p_rarch->current_display_server_data, progress, finished);
- return false;
- }
- bool video_display_server_set_window_decorations(bool on)
- {
- struct rarch_state *p_rarch = &rarch_st;
- if (current_display_server && current_display_server->set_window_decorations)
- return current_display_server->set_window_decorations(
- p_rarch->current_display_server_data, on);
- return false;
- }
- bool video_display_server_set_resolution(unsigned width, unsigned height,
- int int_hz, float hz, int center, int monitor_index, int xoffset, int padjust)
- {
- struct rarch_state *p_rarch = &rarch_st;
- if (current_display_server && current_display_server->set_resolution)
- return current_display_server->set_resolution(
- p_rarch->current_display_server_data, width, height, int_hz,
- hz, center, monitor_index, xoffset, padjust);
- return false;
- }
- bool video_display_server_has_resolution_list(void)
- {
- return (current_display_server
- && current_display_server->get_resolution_list);
- }
- void *video_display_server_get_resolution_list(unsigned *size)
- {
- struct rarch_state *p_rarch = &rarch_st;
- if (video_display_server_has_resolution_list())
- return current_display_server->get_resolution_list(
- p_rarch->current_display_server_data, size);
- return NULL;
- }
- const char *video_display_server_get_output_options(void)
- {
- struct rarch_state *p_rarch = &rarch_st;
- if (current_display_server && current_display_server->get_output_options)
- return current_display_server->get_output_options(p_rarch->current_display_server_data);
- return NULL;
- }
- void video_display_server_set_screen_orientation(enum rotation rotation)
- {
- if (current_display_server && current_display_server->set_screen_orientation)
- {
- struct rarch_state *p_rarch = &rarch_st;
- RARCH_LOG("[Video]: Setting screen orientation to %d.\n", rotation);
- p_rarch->current_screen_orientation = rotation;
- current_display_server->set_screen_orientation(p_rarch->current_display_server_data, rotation);
- }
- }
- bool video_display_server_can_set_screen_orientation(void)
- {
- return (current_display_server && current_display_server->set_screen_orientation);
- }
- enum rotation video_display_server_get_screen_orientation(void)
- {
- struct rarch_state *p_rarch = &rarch_st;
- if (current_display_server && current_display_server->get_screen_orientation)
- return current_display_server->get_screen_orientation(p_rarch->current_display_server_data);
- return ORIENTATION_NORMAL;
- }
- bool video_display_server_get_flags(gfx_ctx_flags_t *flags)
- {
- struct rarch_state *p_rarch = &rarch_st;
- if (!flags || !current_display_server || !current_display_server->get_flags)
- return false;
- flags->flags = current_display_server->get_flags(
- p_rarch->current_display_server_data);
- return true;
- }
- bool video_driver_started_fullscreen(void)
- {
- struct rarch_state *p_rarch = &rarch_st;
- return p_rarch->video_started_fullscreen;
- }
- /* Stub functions */
- static bool get_metrics_null(void *data, enum display_metric_types type,
- float *value) { return false; }
- /**
- * config_get_video_driver_options:
- *
- * Get an enumerated list of all video driver names, separated by '|'.
- *
- * Returns: string listing of all video driver names, separated by '|'.
- **/
- const char* config_get_video_driver_options(void)
- {
- return char_list_new_special(STRING_LIST_VIDEO_DRIVERS, NULL);
- }
- bool video_driver_is_threaded(void)
- {
- #ifdef HAVE_THREADS
- struct rarch_state *p_rarch = &rarch_st;
- #endif
- return VIDEO_DRIVER_IS_THREADED_INTERNAL();
- }
- bool *video_driver_get_threaded(void)
- {
- struct rarch_state *p_rarch = &rarch_st;
- #if defined(__MACH__) && defined(__APPLE__)
- /* TODO/FIXME - force threaded video to disabled on Apple for now
- * until NSWindow/UIWindow concurrency issues are taken care of */
- p_rarch->video_driver_threaded = false;
- #endif
- return &p_rarch->video_driver_threaded;
- }
- void video_driver_set_threaded(bool val)
- {
- struct rarch_state *p_rarch = &rarch_st;
- #if defined(__MACH__) && defined(__APPLE__)
- /* TODO/FIXME - force threaded video to disabled on Apple for now
- * until NSWindow/UIWindow concurrency issues are taken care of */
- p_rarch->video_driver_threaded = false;
- #else
- p_rarch->video_driver_threaded = val;
- #endif
- }
- const char *video_driver_get_ident(void)
- {
- struct rarch_state *p_rarch = &rarch_st;
- if (!p_rarch->current_video)
- return NULL;
- #ifdef HAVE_THREADS
- if (VIDEO_DRIVER_IS_THREADED_INTERNAL())
- {
- const thread_video_t *thr = (const thread_video_t*)VIDEO_DRIVER_GET_PTR_INTERNAL(p_rarch, true);
- if (!thr || !thr->driver)
- return NULL;
- return thr->driver->ident;
- }
- #endif
- return p_rarch->current_video->ident;
- }
- static void video_context_driver_reset(void)
- {
- struct rarch_state *p_rarch = &rarch_st;
- if (!p_rarch->current_video_context.get_metrics)
- p_rarch->current_video_context.get_metrics = get_metrics_null;
- }
- bool video_context_driver_set(const gfx_ctx_driver_t *data)
- {
- struct rarch_state *p_rarch = &rarch_st;
- if (!data)
- return false;
- p_rarch->current_video_context = *data;
- video_context_driver_reset();
- return true;
- }
- static void video_context_driver_destroy_internal(
- gfx_ctx_driver_t *ctx_driver)
- {
- if (!ctx_driver)
- return;
- ctx_driver->init = NULL;
- ctx_driver->bind_api = NULL;
- ctx_driver->swap_interval = NULL;
- ctx_driver->set_video_mode = NULL;
- ctx_driver->get_video_size = NULL;
- ctx_driver->get_video_output_size = NULL;
- ctx_driver->get_video_output_prev = NULL;
- ctx_driver->get_video_output_next = NULL;
- ctx_driver->get_metrics = get_metrics_null;
- ctx_driver->translate_aspect = NULL;
- ctx_driver->update_window_title = NULL;
- ctx_driver->check_window = NULL;
- ctx_driver->set_resize = NULL;
- ctx_driver->suppress_screensaver = NULL;
- ctx_driver->swap_buffers = NULL;
- ctx_driver->input_driver = NULL;
- ctx_driver->get_proc_address = NULL;
- ctx_driver->image_buffer_init = NULL;
- ctx_driver->image_buffer_write = NULL;
- ctx_driver->show_mouse = NULL;
- ctx_driver->ident = NULL;
- ctx_driver->get_flags = NULL;
- ctx_driver->set_flags = NULL;
- ctx_driver->bind_hw_render = NULL;
- ctx_driver->get_context_data = NULL;
- ctx_driver->make_current = NULL;
- }
- void video_context_driver_destroy(void)
- {
- struct rarch_state *p_rarch = &rarch_st;
- video_context_driver_destroy_internal(&p_rarch->current_video_context);
- }
- /**
- * video_driver_get_current_framebuffer:
- *
- * Gets pointer to current hardware renderer framebuffer object.
- * Used by RETRO_ENVIRONMENT_SET_HW_RENDER.
- *
- * Returns: pointer to hardware framebuffer object, otherwise 0.
- **/
- static uintptr_t video_driver_get_current_framebuffer(void)
- {
- struct rarch_state *p_rarch = &rarch_st;
- if ( p_rarch->video_driver_poke
- && p_rarch->video_driver_poke->get_current_framebuffer)
- return p_rarch->video_driver_poke->get_current_framebuffer(
- p_rarch->video_driver_data);
- return 0;
- }
- static retro_proc_address_t video_driver_get_proc_address(const char *sym)
- {
- struct rarch_state *p_rarch = &rarch_st;
- if ( p_rarch->video_driver_poke
- && p_rarch->video_driver_poke->get_proc_address)
- return p_rarch->video_driver_poke->get_proc_address(
- p_rarch->video_driver_data, sym);
- return NULL;
- }
- #ifdef HAVE_VIDEO_FILTER
- static void video_driver_filter_free(void)
- {
- struct rarch_state *p_rarch = &rarch_st;
- if (p_rarch->video_driver_state_filter)
- rarch_softfilter_free(p_rarch->video_driver_state_filter);
- p_rarch->video_driver_state_filter = NULL;
- if (p_rarch->video_driver_state_buffer)
- {
- #ifdef _3DS
- linearFree(p_rarch->video_driver_state_buffer);
- #else
- free(p_rarch->video_driver_state_buffer);
- #endif
- }
- p_rarch->video_driver_state_buffer = NULL;
- p_rarch->video_driver_state_scale = 0;
- p_rarch->video_driver_state_out_bpp = 0;
- p_rarch->video_driver_state_out_rgb32 = false;
- }
- #endif
- #ifdef HAVE_VIDEO_FILTER
- static void video_driver_init_filter(enum retro_pixel_format colfmt_int)
- {
- unsigned pow2_x, pow2_y, maxsize;
- void *buf = NULL;
- struct rarch_state *p_rarch = &rarch_st;
- settings_t *settings = p_rarch->configuration_settings;
- struct retro_game_geometry *geom = &p_rarch->video_driver_av_info.geometry;
- unsigned width = geom->max_width;
- unsigned height = geom->max_height;
- /* Deprecated format. Gets pre-converted. */
- enum retro_pixel_format colfmt =
- (colfmt_int == RETRO_PIXEL_FORMAT_0RGB1555) ?
- RETRO_PIXEL_FORMAT_RGB565 : colfmt_int;
- if (video_driver_is_hw_context())
- {
- RARCH_WARN("[Video]: Cannot use CPU filters when hardware rendering is used.\n");
- return;
- }
- p_rarch->video_driver_state_filter = rarch_softfilter_new(
- settings->paths.path_softfilter_plugin,
- RARCH_SOFTFILTER_THREADS_AUTO, colfmt, width, height);
- if (!p_rarch->video_driver_state_filter)
- {
- RARCH_ERR("[Video]: Failed to load filter.\n");
- return;
- }
- rarch_softfilter_get_max_output_size(
- p_rarch->video_driver_state_filter,
- &width, &height);
- pow2_x = next_pow2(width);
- pow2_y = next_pow2(height);
- maxsize = MAX(pow2_x, pow2_y);
- #ifdef _3DS
- /* On 3DS, video is disabled if the output resolution
- * exceeds 2048x2048. To avoid the user being presented
- * with a black screen, we therefore have to check that
- * the filter upscaling buffer fits within this limit. */
- if (maxsize >= 2048)
- {
- RARCH_ERR("[Video]: Softfilter initialization failed."
- " Upscaling buffer exceeds hardware limitations.\n");
- video_driver_filter_free();
- return;
- }
- #endif
- p_rarch->video_driver_state_scale = maxsize / RARCH_SCALE_BASE;
- p_rarch->video_driver_state_out_rgb32 = rarch_softfilter_get_output_format(
- p_rarch->video_driver_state_filter) == RETRO_PIXEL_FORMAT_XRGB8888;
- p_rarch->video_driver_state_out_bpp =
- p_rarch->video_driver_state_out_rgb32 ?
- sizeof(uint32_t) :
- sizeof(uint16_t);
- /* TODO: Aligned output. */
- #ifdef _3DS
- buf = linearMemAlign(
- width * height * p_rarch->video_driver_state_out_bpp, 0x80);
- #else
- buf = malloc(
- width * height * p_rarch->video_driver_state_out_bpp);
- #endif
- if (!buf)
- {
- RARCH_ERR("[Video]: Softfilter initialization failed.\n");
- video_driver_filter_free();
- return;
- }
- p_rarch->video_driver_state_buffer = buf;
- }
- #endif
- static void video_driver_init_input(input_driver_t *tmp)
- {
- struct rarch_state *p_rarch = &rarch_st;
- input_driver_t **input = &p_rarch->current_input;
- if (*input)
- return;
- /* Video driver didn't provide an input driver,
- * so we use configured one. */
- RARCH_LOG("[Video]: Graphics driver did not initialize an input driver."
- " Attempting to pick a suitable driver.\n");
- if (tmp)
- *input = tmp;
- else
- input_driver_find_driver(p_rarch);
- /* This should never really happen as tmp (driver.input) is always
- * found before this in find_driver_input(), or we have aborted
- * in a similar fashion anyways. */
- if (!p_rarch->current_input || !input_driver_init(p_rarch))
- {
- RARCH_ERR("[Video]: Cannot initialize input driver. Exiting ...\n");
- retroarch_fail(1, "video_driver_init_input()");
- }
- }
- /**
- * video_driver_monitor_compute_fps_statistics:
- *
- * Computes monitor FPS statistics.
- **/
- static void video_driver_monitor_compute_fps_statistics(void)
- {
- double avg_fps = 0.0;
- double stddev = 0.0;
- unsigned samples = 0;
- struct rarch_state *p_rarch = &rarch_st;
- if (p_rarch->video_driver_frame_time_count <
- (2 * MEASURE_FRAME_TIME_SAMPLES_COUNT))
- {
- RARCH_LOG(
- "[Video]: Does not have enough samples for monitor refresh rate"
- " estimation. Requires to run for at least %u frames.\n",
- 2 * MEASURE_FRAME_TIME_SAMPLES_COUNT);
- return;
- }
- if (video_monitor_fps_statistics(&avg_fps, &stddev, &samples))
- {
- RARCH_LOG("[Video]: Average monitor Hz: %.6f Hz. (%.3f %% frame time"
- " deviation, based on %u last samples).\n",
- avg_fps, 100.0f * stddev, samples);
- }
- }
- static void video_driver_pixel_converter_free(
- video_pixel_scaler_t *scalr)
- {
- if (!scalr)
- return;
- if (scalr->scaler)
- {
- scaler_ctx_gen_reset(scalr->scaler);
- free(scalr->scaler);
- }
- if (scalr->scaler_out)
- free(scalr->scaler_out);
- scalr->scaler = NULL;
- scalr->scaler_out = NULL;
- free(scalr);
- }
- static void video_driver_free_hw_context(struct rarch_state *p_rarch)
- {
- VIDEO_DRIVER_CONTEXT_LOCK();
- if (p_rarch->hw_render.context_destroy)
- p_rarch->hw_render.context_destroy();
- memset(&p_rarch->hw_render, 0, sizeof(p_rarch->hw_render));
- VIDEO_DRIVER_CONTEXT_UNLOCK();
- p_rarch->hw_render_context_negotiation = NULL;
- }
- static void video_driver_free_internal(struct rarch_state *p_rarch)
- {
- #ifdef HAVE_THREADS
- bool is_threaded = VIDEO_DRIVER_IS_THREADED_INTERNAL();
- #endif
- #ifdef HAVE_VIDEO_LAYOUT
- video_layout_deinit();
- #endif
- command_event(CMD_EVENT_OVERLAY_DEINIT, NULL);
- if (!video_driver_is_video_cache_context())
- video_driver_free_hw_context(p_rarch);
- if (!(p_rarch->current_input_data == p_rarch->video_driver_data))
- {
- if (p_rarch->current_input)
- if (p_rarch->current_input->free)
- p_rarch->current_input->free(p_rarch->current_input_data);
- if (p_rarch->joypad)
- p_rarch->joypad->destroy();
- p_rarch->joypad = NULL;
- #ifdef HAVE_MFI
- if (p_rarch->sec_joypad)
- p_rarch->sec_joypad->destroy();
- p_rarch->sec_joypad = NULL;
- #endif
- p_rarch->keyboard_mapping_blocked = false;
- p_rarch->current_input_data = NULL;
- }
- if (p_rarch->video_driver_data
- && p_rarch->current_video
- && p_rarch->current_video->free)
- p_rarch->current_video->free(p_rarch->video_driver_data);
- if (p_rarch->video_driver_scaler_ptr)
- video_driver_pixel_converter_free(p_rarch->video_driver_scaler_ptr);
- p_rarch->video_driver_scaler_ptr = NULL;
- #ifdef HAVE_VIDEO_FILTER
- video_driver_filter_free();
- #endif
- dir_free_shader(p_rarch);
- #ifdef HAVE_THREADS
- if (is_threaded)
- return;
- #endif
- video_driver_monitor_compute_fps_statistics();
- }
- static video_pixel_scaler_t *video_driver_pixel_converter_init(
- const enum retro_pixel_format video_driver_pix_fmt,
- struct retro_hw_render_callback *hwr,
- unsigned size)
- {
- void *scalr_out = NULL;
- video_pixel_scaler_t *scalr = NULL;
- struct scaler_ctx *scalr_ctx = NULL;
- /* If pixel format is not 0RGB1555, we don't need to do
- * any internal pixel conversion. */
- if (video_driver_pix_fmt != RETRO_PIXEL_FORMAT_0RGB1555)
- return NULL;
- /* No need to perform pixel conversion for HW rendering contexts. */
- if (hwr && hwr->context_type != RETRO_HW_CONTEXT_NONE)
- return NULL;
- RARCH_WARN("[Video]: 0RGB1555 pixel format is deprecated,"
- " and will be slower. For 15/16-bit, RGB565"
- " format is preferred.\n");
- if (!(scalr = (video_pixel_scaler_t*)malloc(sizeof(*scalr))))
- goto error;
- scalr->scaler = NULL;
- scalr->scaler_out = NULL;
- if (!(scalr_ctx = (struct scaler_ctx*)calloc(1, sizeof(*scalr_ctx))))
- goto error;
- scalr->scaler = scalr_ctx;
- scalr->scaler->scaler_type = SCALER_TYPE_POINT;
- scalr->scaler->in_fmt = SCALER_FMT_0RGB1555;
- /* TODO/FIXME: Pick either ARGB8888 or RGB565 depending on driver. */
- scalr->scaler->out_fmt = SCALER_FMT_RGB565;
- if (!scaler_ctx_gen_filter(scalr_ctx))
- goto error;
- if (!(scalr_out = calloc(sizeof(uint16_t), size * size)))
- goto error;
- scalr->scaler_out = scalr_out;
- return scalr;
- error:
- video_driver_pixel_converter_free(scalr);
- #ifdef HAVE_VIDEO_FILTER
- video_driver_filter_free();
- #endif
- return NULL;
- }
- static void video_driver_set_viewport_config(struct retro_game_geometry *geom,
- settings_t *settings)
- {
- float video_aspect_ratio = settings->floats.video_aspect_ratio;
- if (video_aspect_ratio < 0.0f)
- {
- if (geom->aspect_ratio > 0.0f &&
- settings->bools.video_aspect_ratio_auto)
- aspectratio_lut[ASPECT_RATIO_CONFIG].value = geom->aspect_ratio;
- else
- {
- unsigned base_width = geom->base_width;
- unsigned base_height = geom->base_height;
- /* Get around division by zero errors */
- if (base_width == 0)
- base_width = 1;
- if (base_height == 0)
- base_height = 1;
- aspectratio_lut[ASPECT_RATIO_CONFIG].value =
- (float)base_width / base_height; /* 1:1 PAR. */
- }
- }
- else
- aspectratio_lut[ASPECT_RATIO_CONFIG].value = video_aspect_ratio;
- }
- static void video_driver_set_viewport_square_pixel(struct retro_game_geometry *geom)
- {
- unsigned len, highest, i, aspect_x, aspect_y;
- unsigned width = geom->base_width;
- unsigned height = geom->base_height;
- unsigned int rotation = retroarch_get_rotation();
- if (width == 0 || height == 0)
- return;
- len = MIN(width, height);
- highest = 1;
- for (i = 1; i < len; i++)
- {
- if ((width % i) == 0 && (height % i) == 0)
- highest = i;
- }
- if (rotation % 2)
- {
- aspect_x = height / highest;
- aspect_y = width / highest;
- }
- else
- {
- aspect_x = width / highest;
- aspect_y = height / highest;
- }
- snprintf(aspectratio_lut[ASPECT_RATIO_SQUARE].name,
- sizeof(aspectratio_lut[ASPECT_RATIO_SQUARE].name),
- "1:1 PAR (%u:%u DAR)", aspect_x, aspect_y);
- aspectratio_lut[ASPECT_RATIO_SQUARE].value = (float)aspect_x / aspect_y;
- }
- static bool video_driver_init_internal(bool *video_is_threaded)
- {
- video_info_t video;
- unsigned max_dim, scale, width, height;
- video_viewport_t *custom_vp = NULL;
- input_driver_t *tmp = NULL;
- static uint16_t dummy_pixels[32] = {0};
- struct rarch_state *p_rarch = &rarch_st;
- settings_t *settings = p_rarch->configuration_settings;
- struct retro_game_geometry *geom = &p_rarch->video_driver_av_info.geometry;
- const enum retro_pixel_format
- video_driver_pix_fmt = p_rarch->video_driver_pix_fmt;
- #ifdef HAVE_VIDEO_FILTER
- const char *path_softfilter_plugin = settings->paths.path_softfilter_plugin;
- if (!string_is_empty(path_softfilter_plugin))
- video_driver_init_filter(video_driver_pix_fmt);
- #endif
- max_dim = MAX(geom->max_width, geom->max_height);
- scale = next_pow2(max_dim) / RARCH_SCALE_BASE;
- scale = MAX(scale, 1);
- #ifdef HAVE_VIDEO_FILTER
- if (p_rarch->video_driver_state_filter)
- scale = p_rarch->video_driver_state_scale;
- #endif
- /* Update core-dependent aspect ratio values. */
- video_driver_set_viewport_square_pixel(geom);
- video_driver_set_viewport_core();
- video_driver_set_viewport_config(geom, settings);
- /* Update CUSTOM viewport. */
- custom_vp = video_viewport_get_custom();
- if (settings->uints.video_aspect_ratio_idx == ASPECT_RATIO_CUSTOM)
- {
- float default_aspect = aspectratio_lut[ASPECT_RATIO_CORE].value;
- aspectratio_lut[ASPECT_RATIO_CUSTOM].value =
- (custom_vp->width && custom_vp->height) ?
- (float)custom_vp->width / custom_vp->height : default_aspect;
- }
- {
- /* Guard against aspect ratio index possibly being out of bounds */
- unsigned new_aspect_idx = settings->uints.video_aspect_ratio_idx;
- if (new_aspect_idx > ASPECT_RATIO_END)
- new_aspect_idx = settings->uints.video_aspect_ratio_idx = 0;
- video_driver_set_aspect_ratio_value(
- aspectratio_lut[new_aspect_idx].value);
- }
- if (settings->bools.video_fullscreen|| p_rarch->rarch_force_fullscreen)
- {
- width = settings->uints.video_fullscreen_x;
- height = settings->uints.video_fullscreen_y;
- }
- else
- {
- /* TODO: remove when the new window resizing core is hooked */
- if (settings->bools.video_window_save_positions &&
- (settings->uints.window_position_width ||
- settings->uints.window_position_height))
- {
- width = settings->uints.window_position_width;
- height = settings->uints.window_position_height;
- }
- else
- {
- float video_scale = settings->floats.video_scale;
- if (settings->bools.video_force_aspect)
- {
- /* Do rounding here to simplify integer scale correctness. */
- unsigned base_width =
- roundf(geom->base_height * p_rarch->video_driver_aspect_ratio);
- width = roundf(base_width * video_scale);
- }
- else
- width = roundf(geom->base_width * video_scale);
- height = roundf(geom->base_height * video_scale);
- }
- }
- if (width && height)
- RARCH_LOG("[Video]: Video @ %ux%u\n", width, height);
- else
- RARCH_LOG("[Video]: Video @ fullscreen\n");
- video_driver_display_type_set(RARCH_DISPLAY_NONE);
- video_driver_display_set(0);
- video_driver_display_userdata_set(0);
- video_driver_window_set(0);
- p_rarch->video_driver_scaler_ptr = video_driver_pixel_converter_init(
- p_rarch->video_driver_pix_fmt,
- VIDEO_DRIVER_GET_HW_CONTEXT_INTERNAL(p_rarch),
- RARCH_SCALE_BASE * scale);
- video.width = width;
- video.height = height;
- video.fullscreen = settings->bools.video_fullscreen ||
- p_rarch->rarch_force_fullscreen;
- video.vsync = settings->bools.video_vsync &&
- !p_rarch->runloop_force_nonblock;
- video.force_aspect = settings->bools.video_force_aspect;
- video.font_enable = settings->bools.video_font_enable;
- video.swap_interval = settings->uints.video_swap_interval;
- video.adaptive_vsync = settings->bools.video_adaptive_vsync;
- #ifdef GEKKO
- video.viwidth = settings->uints.video_viwidth;
- video.vfilter = settings->bools.video_vfilter;
- #endif
- video.smooth = settings->bools.video_smooth;
- video.ctx_scaling = settings->bools.video_ctx_scaling;
- video.input_scale = scale;
- video.font_size = settings->floats.video_font_size;
- video.path_font = settings->paths.path_font;
- #ifdef HAVE_VIDEO_FILTER
- video.rgb32 =
- p_rarch->video_driver_state_filter ?
- p_rarch->video_driver_state_out_rgb32 :
- (video_driver_pix_fmt == RETRO_PIXEL_FORMAT_XRGB8888);
- #else
- video.rgb32 =
- (video_driver_pix_fmt == RETRO_PIXEL_FORMAT_XRGB8888);
- #endif
- video.parent = 0;
- p_rarch->video_started_fullscreen = video.fullscreen;
- /* Reset video frame count */
- p_rarch->video_driver_frame_count = 0;
- tmp = p_rarch->current_input;
- /* Need to grab the "real" video driver interface on a reinit. */
- video_driver_find_driver(p_rarch);
- #ifdef HAVE_THREADS
- video.is_threaded = VIDEO_DRIVER_IS_THREADED_INTERNAL();
- *video_is_threaded = video.is_threaded;
- if (video.is_threaded)
- {
- bool ret;
- /* Can't do hardware rendering with threaded driver currently. */
- RARCH_LOG("[Video]: Starting threaded video driver ...\n");
- ret = video_init_thread((const video_driver_t**)&p_rarch->current_video,
- &p_rarch->video_driver_data,
- &p_rarch->current_input,
- (void**)&p_rarch->current_input_data,
- p_rarch->current_video,
- video);
- if (!ret)
- {
- RARCH_ERR("[Video]: Cannot open threaded video driver ... Exiting ...\n");
- goto error;
- }
- }
- else
- #endif
- p_rarch->video_driver_data = p_rarch->current_video->init(
- &video,
- &p_rarch->current_input,
- (void**)&p_rarch->current_input_data);
- if (!p_rarch->video_driver_data)
- {
- RARCH_ERR("[Video]: Cannot open video driver ... Exiting ...\n");
- goto error;
- }
- p_rarch->video_driver_poke = NULL;
- if (p_rarch->current_video->poke_interface)
- p_rarch->current_video->poke_interface(
- p_rarch->video_driver_data, &p_rarch->video_driver_poke);
- if (p_rarch->current_video->viewport_info &&
- (!custom_vp->width ||
- !custom_vp->height))
- {
- /* Force custom viewport to have sane parameters. */
- custom_vp->width = width;
- custom_vp->height = height;
- video_driver_get_viewport_info(custom_vp);
- }
- video_driver_set_rotation(retroarch_get_rotation() % 4);
- p_rarch->current_video->suppress_screensaver(p_rarch->video_driver_data,
- settings->bools.ui_suspend_screensaver_enable);
- video_driver_init_input(tmp);
- #ifdef HAVE_OVERLAY
- retroarch_overlay_deinit(p_rarch);
- retroarch_overlay_init(p_rarch);
- #endif
- #ifdef HAVE_VIDEO_LAYOUT
- if (settings->bools.video_layout_enable)
- {
- video_layout_init(p_rarch->video_driver_data,
- video_driver_layout_render_interface());
- video_layout_load(settings->paths.path_video_layout);
- video_layout_view_select(settings->uints.video_layout_selected_view);
- }
- #endif
- if (!p_rarch->current_core.game_loaded)
- video_driver_cached_frame_set(&dummy_pixels, 4, 4, 8);
- #if defined(PSP)
- video_driver_set_texture_frame(&dummy_pixels, false, 1, 1, 1.0f);
- #endif
- video_context_driver_reset();
- video_display_server_init(p_rarch->video_driver_display_type);
- if ((enum rotation)settings->uints.screen_orientation != ORIENTATION_NORMAL)
- video_display_server_set_screen_orientation((enum rotation)settings->uints.screen_orientation);
- /* Ensure that we preserve the 'grab mouse'
- * state if it was enabled prior to driver
- * (re-)initialisation */
- if (p_rarch->input_driver_grab_mouse_state)
- {
- video_driver_hide_mouse();
- input_driver_grab_mouse(p_rarch);
- }
- else if (video.fullscreen)
- {
- video_driver_hide_mouse();
- if (!settings->bools.video_windowed_fullscreen)
- input_driver_grab_mouse(p_rarch);
- }
- return true;
- error:
- retroarch_fail(1, "init_video()");
- return false;
- }
- void video_driver_set_viewport(unsigned width, unsigned height,
- bool force_fullscreen, bool allow_rotate)
- {
- struct rarch_state *p_rarch = &rarch_st;
- if (p_rarch->current_video && p_rarch->current_video->set_viewport)
- p_rarch->current_video->set_viewport(
- p_rarch->video_driver_data, width, height,
- force_fullscreen, allow_rotate);
- }
- bool video_driver_set_rotation(unsigned rotation)
- {
- struct rarch_state *p_rarch = &rarch_st;
- if (!p_rarch->current_video || !p_rarch->current_video->set_rotation)
- return false;
- p_rarch->current_video->set_rotation(p_rarch->video_driver_data, rotation);
- return true;
- }
- bool video_driver_set_video_mode(unsigned width,
- unsigned height, bool fullscreen)
- {
- struct rarch_state *p_rarch = &rarch_st;
- if ( p_rarch->video_driver_poke &&
- p_rarch->video_driver_poke->set_video_mode)
- {
- p_rarch->video_driver_poke->set_video_mode(p_rarch->video_driver_data,
- width, height, fullscreen);
- return true;
- }
- return false;
- }
- bool video_driver_get_video_output_size(unsigned *width, unsigned *height)
- {
- struct rarch_state *p_rarch = &rarch_st;
- if (!p_rarch->video_driver_poke || !p_rarch->video_driver_poke->get_video_output_size)
- return false;
- p_rarch->video_driver_poke->get_video_output_size(p_rarch->video_driver_data,
- width, height);
- return true;
- }
- /* Draw text on top of the screen */
- void gfx_display_draw_text(
- const font_data_t *font, const char *text,
- float x, float y, int width, int height,
- uint32_t color, enum text_alignment text_align,
- float scale, bool shadows_enable, float shadow_offset,
- bool draw_outside)
- {
- struct font_params params;
- struct rarch_state *p_rarch = &rarch_st;
- if ((color & 0x000000FF) == 0)
- return;
- /* Don't draw outside of the screen */
- if (!draw_outside &&
- ((x < -64 || x > width + 64)
- || (y < -64 || y > height + 64))
- )
- return;
- params.x = x / width;
- params.y = 1.0f - y / height;
- params.scale = scale;
- params.drop_mod = 0.0f;
- params.drop_x = 0.0f;
- params.drop_y = 0.0f;
- params.color = color;
- params.full_screen = true;
- params.text_align = text_align;
- if (shadows_enable)
- {
- params.drop_x = shadow_offset;
- params.drop_y = -shadow_offset;
- params.drop_alpha = 0.35f;
- }
- if (p_rarch->video_driver_poke && p_rarch->video_driver_poke->set_osd_msg)
- p_rarch->video_driver_poke->set_osd_msg(p_rarch->video_driver_data,
- text, ¶ms, (void*)font);
- }
- void video_driver_set_texture_enable(bool enable, bool fullscreen)
- {
- struct rarch_state *p_rarch = &rarch_st;
- if (p_rarch->video_driver_poke && p_rarch->video_driver_poke->set_texture_enable)
- p_rarch->video_driver_poke->set_texture_enable(p_rarch->video_driver_data,
- enable, fullscreen);
- }
- void video_driver_set_texture_frame(const void *frame, bool rgb32,
- unsigned width, unsigned height, float alpha)
- {
- struct rarch_state *p_rarch = &rarch_st;
- if (p_rarch->video_driver_poke &&
- p_rarch->video_driver_poke->set_texture_frame)
- p_rarch->video_driver_poke->set_texture_frame(p_rarch->video_driver_data,
- frame, rgb32, width, height, alpha);
- }
- #ifdef HAVE_OVERLAY
- static bool video_driver_overlay_interface(
- const video_overlay_interface_t **iface)
- {
- struct rarch_state *p_rarch = &rarch_st;
- if (!p_rarch->current_video || !p_rarch->current_video->overlay_interface)
- return false;
- p_rarch->current_video->overlay_interface(p_rarch->video_driver_data, iface);
- return true;
- }
- #endif
- #ifdef HAVE_VIDEO_LAYOUT
- const video_layout_render_interface_t *video_driver_layout_render_interface(void)
- {
- struct rarch_state *p_rarch = &rarch_st;
- if ( !p_rarch->current_video ||
- !p_rarch->current_video->video_layout_render_interface)
- return NULL;
- return p_rarch->current_video->video_layout_render_interface(
- p_rarch->video_driver_data);
- }
- #endif
- void *video_driver_read_frame_raw(unsigned *width,
- unsigned *height, size_t *pitch)
- {
- struct rarch_state *p_rarch = &rarch_st;
- if (!p_rarch->current_video || !p_rarch->current_video->read_frame_raw)
- return NULL;
- return p_rarch->current_video->read_frame_raw(
- p_rarch->video_driver_data, width,
- height, pitch);
- }
- void video_driver_set_filtering(unsigned index, bool smooth, bool ctx_scaling)
- {
- struct rarch_state *p_rarch = &rarch_st;
- if (p_rarch->video_driver_poke && p_rarch->video_driver_poke->set_filtering)
- p_rarch->video_driver_poke->set_filtering(p_rarch->video_driver_data,
- index, smooth, ctx_scaling);
- }
- void video_driver_cached_frame_set(const void *data, unsigned width,
- unsigned height, size_t pitch)
- {
- struct rarch_state *p_rarch = &rarch_st;
- if (data)
- p_rarch->frame_cache_data = data;
- p_rarch->frame_cache_width = width;
- p_rarch->frame_cache_height = height;
- p_rarch->frame_cache_pitch = pitch;
- }
- void video_driver_cached_frame_get(const void **data, unsigned *width,
- unsigned *height, size_t *pitch)
- {
- struct rarch_state *p_rarch = &rarch_st;
- if (data)
- *data = p_rarch->frame_cache_data;
- if (width)
- *width = p_rarch->frame_cache_width;
- if (height)
- *height = p_rarch->frame_cache_height;
- if (pitch)
- *pitch = p_rarch->frame_cache_pitch;
- }
- void video_driver_get_size(unsigned *width, unsigned *height)
- {
- struct rarch_state *p_rarch = &rarch_st;
- #ifdef HAVE_THREADS
- bool is_threaded = VIDEO_DRIVER_IS_THREADED_INTERNAL();
- VIDEO_DRIVER_THREADED_LOCK(is_threaded);
- #endif
- if (width)
- *width = p_rarch->video_driver_width;
- if (height)
- *height = p_rarch->video_driver_height;
- #ifdef HAVE_THREADS
- VIDEO_DRIVER_THREADED_UNLOCK(is_threaded);
- #endif
- }
- void video_driver_set_size(unsigned width, unsigned height)
- {
- struct rarch_state *p_rarch = &rarch_st;
- #ifdef HAVE_THREADS
- bool is_threaded = VIDEO_DRIVER_IS_THREADED_INTERNAL();
- VIDEO_DRIVER_THREADED_LOCK(is_threaded);
- #endif
- p_rarch->video_driver_width = width;
- p_rarch->video_driver_height = height;
- #ifdef HAVE_THREADS
- VIDEO_DRIVER_THREADED_UNLOCK(is_threaded);
- #endif
- }
- /**
- * video_monitor_set_refresh_rate:
- * @hz : New refresh rate for monitor.
- *
- * Sets monitor refresh rate to new value.
- **/
- void video_monitor_set_refresh_rate(float hz)
- {
- char msg[128];
- struct rarch_state *p_rarch = &rarch_st;
- settings_t *settings = p_rarch->configuration_settings;
- snprintf(msg, sizeof(msg),
- "[Video]: Setting refresh rate to: %.3f Hz.", hz);
- if (settings->bools.notification_show_refresh_rate)
- runloop_msg_queue_push(msg, 1, 180, false, NULL,
- MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO);
- RARCH_LOG("%s\n", msg);
- configuration_set_float(settings,
- settings->floats.video_refresh_rate,
- hz);
- /* Without CMD_EVENT_REINIT vulkan will not switch rate properly,
- * and with other drivers it prevents switching properly.. */
- if (string_is_equal(settings->arrays.video_driver, "vulkan"))
- command_event(CMD_EVENT_REINIT, NULL);
- }
- /**
- * video_monitor_fps_statistics
- * @refresh_rate : Monitor refresh rate.
- * @deviation : Deviation from measured refresh rate.
- * @sample_points : Amount of sampled points.
- *
- * Gets the monitor FPS statistics based on the current
- * runtime.
- *
- * Returns: true (1) on success.
- * false (0) if:
- * a) threaded video mode is enabled
- * b) less than 2 frame time samples.
- * c) FPS monitor enable is off.
- **/
- bool video_monitor_fps_statistics(double *refresh_rate,
- double *deviation, unsigned *sample_points)
- {
- unsigned i;
- retro_time_t accum = 0;
- retro_time_t avg = 0;
- retro_time_t accum_var = 0;
- unsigned samples = 0;
- struct rarch_state *p_rarch = &rarch_st;
- #ifdef HAVE_THREADS
- if (VIDEO_DRIVER_IS_THREADED_INTERNAL())
- return false;
- #endif
- samples = MIN(MEASURE_FRAME_TIME_SAMPLES_COUNT,
- (unsigned)p_rarch->video_driver_frame_time_count);
- if (samples < 2)
- return false;
- /* Measure statistics on frame time (microsecs), *not* FPS. */
- for (i = 0; i < samples; i++)
- {
- accum += p_rarch->video_driver_frame_time_samples[i];
- #if 0
- RARCH_LOG("[Video]: Interval #%u: %d usec / frame.\n",
- i, (int)frame_time_samples[i]);
- #endif
- }
- avg = accum / samples;
- /* Drop first measurement. It is likely to be bad. */
- for (i = 0; i < samples; i++)
- {
- retro_time_t diff = p_rarch->video_driver_frame_time_samples[i] - avg;
- accum_var += diff * diff;
- }
- *deviation = sqrt((double)accum_var / (samples - 1)) / avg;
- if (refresh_rate)
- *refresh_rate = 1000000.0 / avg;
- if (sample_points)
- *sample_points = samples;
- return true;
- }
- float video_driver_get_aspect_ratio(void)
- {
- struct rarch_state *p_rarch = &rarch_st;
- return p_rarch->video_driver_aspect_ratio;
- }
- void video_driver_set_aspect_ratio_value(float value)
- {
- struct rarch_state *p_rarch = &rarch_st;
- p_rarch->video_driver_aspect_ratio = value;
- }
- enum retro_pixel_format video_driver_get_pixel_format(void)
- {
- struct rarch_state *p_rarch = &rarch_st;
- return p_rarch->video_driver_pix_fmt;
- }
- /**
- * video_driver_cached_frame:
- *
- * Renders the current video frame.
- **/
- void video_driver_cached_frame(void)
- {
- struct rarch_state *p_rarch = &rarch_st;
- void *recording = p_rarch->recording_data;
- struct retro_callbacks *cbs = &p_rarch->retro_ctx;
- /* Cannot allow recording when pushing duped frames. */
- p_rarch->recording_data = NULL;
- if (p_rarch->current_core.inited)
- cbs->frame_cb(
- (p_rarch->frame_cache_data != RETRO_HW_FRAME_BUFFER_VALID)
- ? p_rarch->frame_cache_data : NULL,
- p_rarch->frame_cache_width,
- p_rarch->frame_cache_height,
- p_rarch->frame_cache_pitch);
- p_rarch->recording_data = recording;
- }
- static void video_driver_monitor_adjust_system_rates(
- struct rarch_state *p_rarch)
- {
- float timing_skew = 0.0f;
- settings_t *settings = p_rarch->configuration_settings;
- const struct retro_system_timing *info = (const struct retro_system_timing*)
- &p_rarch->video_driver_av_info.timing;
- float video_refresh_rate = settings->floats.video_refresh_rate;
- bool vrr_runloop_enable = settings->bools.vrr_runloop_enable;
- float audio_max_timing_skew = settings->floats.audio_max_timing_skew;
- float timing_skew_hz = video_refresh_rate;
- if (!info || info->fps <= 0.0)
- return;
- p_rarch->video_driver_core_hz = info->fps;
- if (p_rarch->video_driver_crt_switching_active)
- timing_skew_hz = p_rarch->video_driver_core_hz;
- timing_skew = fabs(
- 1.0f - info->fps / timing_skew_hz);
- if (!vrr_runloop_enable)
- {
- /* We don't want to adjust pitch too much. If we have extreme cases,
- * just don't readjust at all. */
- if (timing_skew <= audio_max_timing_skew)
- return;
- RARCH_LOG("[Video]: Timings deviate too much. Will not adjust."
- " (Display = %.2f Hz, Game = %.2f Hz)\n",
- video_refresh_rate,
- (float)info->fps);
- }
- if (info->fps <= timing_skew_hz)
- return;
- /* We won't be able to do VSync reliably when game FPS > monitor FPS. */
- p_rarch->runloop_force_nonblock = true;
- RARCH_LOG("[Video]: Game FPS > Monitor FPS. Cannot rely on VSync.\n");
- }
- static void video_driver_lock_new(void)
- {
- #ifdef HAVE_THREADS
- struct rarch_state *p_rarch = &rarch_st;
- #endif
- VIDEO_DRIVER_LOCK_FREE();
- #ifdef HAVE_THREADS
- if (!p_rarch->display_lock)
- p_rarch->display_lock = slock_new();
- retro_assert(p_rarch->display_lock);
- if (!p_rarch->context_lock)
- p_rarch->context_lock = slock_new();
- retro_assert(p_rarch->context_lock);
- #endif
- }
- void video_driver_set_cached_frame_ptr(const void *data)
- {
- struct rarch_state *p_rarch = &rarch_st;
- if (data)
- p_rarch->frame_cache_data = data;
- }
- void video_driver_set_stub_frame(void)
- {
- struct rarch_state *p_rarch = &rarch_st;
- p_rarch->frame_bak = p_rarch->current_video->frame;
- p_rarch->current_video->frame = video_null.frame;
- }
- void video_driver_unset_stub_frame(void)
- {
- struct rarch_state *p_rarch = &rarch_st;
- if (p_rarch->frame_bak)
- p_rarch->current_video->frame = p_rarch->frame_bak;
- p_rarch->frame_bak = NULL;
- }
- bool video_driver_supports_viewport_read(void)
- {
- struct rarch_state *p_rarch = &rarch_st;
- return p_rarch->current_video->read_viewport
- && p_rarch->current_video->viewport_info;
- }
- bool video_driver_prefer_viewport_read(void)
- {
- struct rarch_state *p_rarch = &rarch_st;
- settings_t *settings = p_rarch->configuration_settings;
- #ifdef HAVE_SCREENSHOTS
- bool video_gpu_screenshot = settings->bools.video_gpu_screenshot;
- if (video_gpu_screenshot)
- return true;
- #endif
- return (video_driver_is_hw_context() &&
- !p_rarch->current_video->read_frame_raw);
- }
- bool video_driver_supports_read_frame_raw(void)
- {
- struct rarch_state *p_rarch = &rarch_st;
- if (p_rarch->current_video->read_frame_raw)
- return true;
- return false;
- }
- void video_driver_set_viewport_core(void)
- {
- struct rarch_state *p_rarch = &rarch_st;
- struct retro_game_geometry *geom = &p_rarch->video_driver_av_info.geometry;
- if (!geom || geom->base_width <= 0.0f || geom->base_height <= 0.0f)
- return;
- /* Fallback to 1:1 pixel ratio if none provided */
- if (geom->aspect_ratio > 0.0f)
- aspectratio_lut[ASPECT_RATIO_CORE].value = geom->aspect_ratio;
- else
- aspectratio_lut[ASPECT_RATIO_CORE].value =
- (float)geom->base_width / geom->base_height;
- }
- void video_driver_reset_custom_viewport(void)
- {
- struct video_viewport *custom_vp = video_viewport_get_custom();
- custom_vp->width = 0;
- custom_vp->height = 0;
- custom_vp->x = 0;
- custom_vp->y = 0;
- }
- void video_driver_set_rgba(void)
- {
- struct rarch_state *p_rarch = &rarch_st;
- VIDEO_DRIVER_LOCK();
- p_rarch->video_driver_use_rgba = true;
- VIDEO_DRIVER_UNLOCK();
- }
- void video_driver_unset_rgba(void)
- {
- struct rarch_state *p_rarch = &rarch_st;
- VIDEO_DRIVER_LOCK();
- p_rarch->video_driver_use_rgba = false;
- VIDEO_DRIVER_UNLOCK();
- }
- bool video_driver_supports_rgba(void)
- {
- bool tmp;
- struct rarch_state *p_rarch = &rarch_st;
- VIDEO_DRIVER_LOCK();
- tmp = p_rarch->video_driver_use_rgba;
- VIDEO_DRIVER_UNLOCK();
- return tmp;
- }
- bool video_driver_get_next_video_out(void)
- {
- struct rarch_state *p_rarch = &rarch_st;
- if ( !p_rarch->video_driver_poke
- || !p_rarch->video_driver_poke->get_video_output_next
- )
- return false;
- p_rarch->video_driver_poke->get_video_output_next(
- p_rarch->video_driver_data);
- return true;
- }
- bool video_driver_get_prev_video_out(void)
- {
- struct rarch_state *p_rarch = &rarch_st;
- if (
- !p_rarch->video_driver_poke
- || !p_rarch->video_driver_poke->get_video_output_prev
- )
- return false;
- p_rarch->video_driver_poke->get_video_output_prev(
- p_rarch->video_driver_data);
- return true;
- }
- void video_driver_monitor_reset(void)
- {
- struct rarch_state *p_rarch = &rarch_st;
- p_rarch->video_driver_frame_time_count = 0;
- }
- void video_driver_set_aspect_ratio(void)
- {
- struct rarch_state *p_rarch = &rarch_st;
- settings_t *settings = p_rarch->configuration_settings;
- unsigned aspect_ratio_idx = settings->uints.video_aspect_ratio_idx;
- switch (aspect_ratio_idx)
- {
- case ASPECT_RATIO_SQUARE:
- video_driver_set_viewport_square_pixel(&p_rarch->video_driver_av_info.geometry);
- break;
- case ASPECT_RATIO_CORE:
- video_driver_set_viewport_core();
- break;
- case ASPECT_RATIO_CONFIG:
- video_driver_set_viewport_config(&p_rarch->video_driver_av_info.geometry, settings);
- break;
- default:
- break;
- }
- video_driver_set_aspect_ratio_value(
- aspectratio_lut[aspect_ratio_idx].value);
- if ( p_rarch->video_driver_poke &&
- p_rarch->video_driver_poke->set_aspect_ratio)
- p_rarch->video_driver_poke->set_aspect_ratio(
- p_rarch->video_driver_data, aspect_ratio_idx);
- }
- void video_driver_update_viewport(
- struct video_viewport* vp, bool force_full, bool keep_aspect)
- {
- struct rarch_state *p_rarch = &rarch_st;
- float device_aspect = (float)vp->full_width / vp->full_height;
- settings_t *settings = p_rarch->configuration_settings;
- bool video_scale_integer = settings->bools.video_scale_integer;
- unsigned video_aspect_ratio_idx = settings->uints.video_aspect_ratio_idx;
- float video_driver_aspect_ratio = p_rarch->video_driver_aspect_ratio;
- vp->x = 0;
- vp->y = 0;
- vp->width = vp->full_width;
- vp->height = vp->full_height;
- if (video_scale_integer && !force_full)
- video_viewport_get_scaled_integer(
- vp,
- vp->full_width,
- vp->full_height,
- video_driver_aspect_ratio, keep_aspect);
- else if (keep_aspect && !force_full)
- {
- float desired_aspect = video_driver_aspect_ratio;
- #if defined(HAVE_MENU)
- if (video_aspect_ratio_idx == ASPECT_RATIO_CUSTOM)
- {
- const struct video_viewport* custom = video_viewport_get_custom();
- vp->x = custom->x;
- vp->y = custom->y;
- vp->width = custom->width;
- vp->height = custom->height;
- }
- else
- #endif
- {
- float delta;
- if (fabsf(device_aspect - desired_aspect) < 0.0001f)
- {
- /* If the aspect ratios of screen and desired aspect
- * ratio are sufficiently equal (floating point stuff),
- * assume they are actually equal.
- */
- }
- else if (device_aspect > desired_aspect)
- {
- delta = (desired_aspect / device_aspect - 1.0f)
- / 2.0f + 0.5f;
- vp->x = (int)roundf(vp->full_width * (0.5f - delta));
- vp->width = (unsigned)roundf(2.0f * vp->full_width * delta);
- vp->y = 0;
- vp->height = vp->full_height;
- }
- else
- {
- vp->x = 0;
- vp->width = vp->full_width;
- delta = (device_aspect / desired_aspect - 1.0f)
- / 2.0f + 0.5f;
- vp->y = (int)roundf(vp->full_height * (0.5f - delta));
- vp->height = (unsigned)roundf(2.0f * vp->full_height * delta);
- }
- }
- }
- #if defined(RARCH_MOBILE)
- /* In portrait mode, we want viewport to gravitate to top of screen. */
- if (device_aspect < 1.0f)
- vp->y = 0;
- #endif
- }
- void video_driver_show_mouse(void)
- {
- struct rarch_state *p_rarch = &rarch_st;
- if (p_rarch->video_driver_poke && p_rarch->video_driver_poke->show_mouse)
- p_rarch->video_driver_poke->show_mouse(p_rarch->video_driver_data, true);
- }
- void video_driver_hide_mouse(void)
- {
- struct rarch_state *p_rarch = &rarch_st;
- if (p_rarch->video_driver_poke && p_rarch->video_driver_poke->show_mouse)
- p_rarch->video_driver_poke->show_mouse(p_rarch->video_driver_data, false);
- }
- static void video_driver_save_as_cached(struct rarch_state *p_rarch,
- settings_t *settings, const char *rdr_context_name)
- {
- RARCH_LOG("[Video]: \"%s\" saved as cached driver.\n",
- settings->arrays.video_driver);
- strlcpy(p_rarch->cached_video_driver,
- settings->arrays.video_driver,
- sizeof(p_rarch->cached_video_driver));
- configuration_set_string(settings,
- settings->arrays.video_driver,
- rdr_context_name);
- }
- static void video_driver_restore_cached(struct rarch_state *p_rarch,
- settings_t *settings)
- {
- if (p_rarch->cached_video_driver[0])
- {
- configuration_set_string(settings,
- settings->arrays.video_driver, p_rarch->cached_video_driver);
- p_rarch->cached_video_driver[0] = 0;
- RARCH_LOG("[Video]: Restored video driver to \"%s\".\n",
- settings->arrays.video_driver);
- }
- }
- static bool video_driver_find_driver(struct rarch_state *p_rarch)
- {
- int i;
- settings_t *settings = p_rarch->configuration_settings;
- if (video_driver_is_hw_context())
- {
- struct retro_hw_render_callback *hwr =
- VIDEO_DRIVER_GET_HW_CONTEXT_INTERNAL(p_rarch);
- int rdr_major = hwr->version_major;
- int rdr_minor = hwr->version_minor;
- const char *rdr_context_name = hw_render_context_name(hwr->context_type, rdr_major, rdr_minor);
- enum retro_hw_context_type rdr_type = hw_render_context_type(rdr_context_name);
- p_rarch->current_video = NULL;
- if (hwr)
- {
- switch (rdr_type)
- {
- case RETRO_HW_CONTEXT_OPENGL_CORE:
- case RETRO_HW_CONTEXT_VULKAN:
- case RETRO_HW_CONTEXT_DIRECT3D:
- #if defined(HAVE_VULKAN) || defined(HAVE_D3D11) || defined(HAVE_D3D9) || defined(HAVE_OPENGL_CORE)
- RARCH_LOG("[Video]: Using HW render, %s driver forced.\n",
- rdr_context_name);
- if (!string_is_equal(settings->arrays.video_driver,
- rdr_context_name))
- video_driver_save_as_cached(p_rarch, settings, rdr_context_name);
- p_rarch->current_video = hw_render_context_driver(rdr_type, rdr_major, rdr_minor);
- return true;
- #else
- break;
- #endif
- case RETRO_HW_CONTEXT_OPENGL:
- #if defined(HAVE_OPENGL)
- RARCH_LOG("[Video]: Using HW render, OpenGL driver forced.\n");
- /* If we have configured one of the HW render
- * capable GL drivers, go with that. */
- #if defined(HAVE_OPENGL_CORE)
- if ( !string_is_equal(settings->arrays.video_driver, "gl") &&
- !string_is_equal(settings->arrays.video_driver, "glcore"))
- {
- video_driver_save_as_cached(p_rarch, settings, "glcore");
- p_rarch->current_video = &video_gl_core;
- return true;
- }
- #else
- if ( !string_is_equal(settings->arrays.video_driver, "gl"))
- {
- video_driver_save_as_cached(p_rarch, settings, "gl");
- p_rarch->current_video = &video_gl2;
- return true;
- }
- #endif
- RARCH_LOG("[Video]: Using configured \"%s\""
- " driver for GL HW render.\n",
- settings->arrays.video_driver);
- break;
- #endif
- default:
- case RETRO_HW_CONTEXT_NONE:
- break;
- }
- }
- }
- if (frontend_driver_has_get_video_driver_func())
- {
- if ((p_rarch->current_video = (video_driver_t*)
- frontend_driver_get_video_driver()))
- return true;
- RARCH_WARN("[Video]: Frontend supports get_video_driver() but did not specify one.\n");
- }
- i = (int)driver_find_index(
- "video_driver",
- settings->arrays.video_driver);
- if (i >= 0)
- p_rarch->current_video = (video_driver_t*)video_drivers[i];
- else
- {
- if (verbosity_is_enabled())
- {
- unsigned d;
- RARCH_ERR("Couldn't find any video driver named \"%s\"\n",
- settings->arrays.video_driver);
- RARCH_LOG_OUTPUT("Available video drivers are:\n");
- for (d = 0; video_drivers[d]; d++)
- RARCH_LOG_OUTPUT("\t%s\n", video_drivers[d]->ident);
- RARCH_WARN("Going to default to first video driver...\n");
- }
- if (!(p_rarch->current_video = (video_driver_t*)video_drivers[0]))
- retroarch_fail(1, "find_video_driver()");
- }
- return true;
- }
- void video_driver_apply_state_changes(void)
- {
- struct rarch_state *p_rarch = &rarch_st;
- if ( p_rarch->video_driver_poke &&
- p_rarch->video_driver_poke->apply_state_changes)
- p_rarch->video_driver_poke->apply_state_changes(
- p_rarch->video_driver_data);
- }
- bool video_driver_read_viewport(uint8_t *buffer, bool is_idle)
- {
- struct rarch_state *p_rarch = &rarch_st;
- if ( p_rarch->current_video->read_viewport
- && p_rarch->current_video->read_viewport(
- p_rarch->video_driver_data, buffer, is_idle))
- return true;
- return false;
- }
- static void video_driver_reinit_context(struct rarch_state *p_rarch,
- int flags)
- {
- /* RARCH_DRIVER_CTL_UNINIT clears the callback struct so we
- * need to make sure to keep a copy */
- struct retro_hw_render_callback hwr_copy;
- struct retro_hw_render_callback *hwr =
- VIDEO_DRIVER_GET_HW_CONTEXT_INTERNAL(p_rarch);
- const struct retro_hw_render_context_negotiation_interface *iface =
- p_rarch->hw_render_context_negotiation;
- memcpy(&hwr_copy, hwr, sizeof(hwr_copy));
- driver_uninit(p_rarch, flags);
- memcpy(hwr, &hwr_copy, sizeof(*hwr));
- p_rarch->hw_render_context_negotiation = iface;
- drivers_init(p_rarch, flags);
- }
- void video_driver_reinit(int flags)
- {
- struct rarch_state *p_rarch = &rarch_st;
- struct retro_hw_render_callback *hwr =
- VIDEO_DRIVER_GET_HW_CONTEXT_INTERNAL(p_rarch);
- p_rarch->video_driver_cache_context = (hwr->cache_context != false);
- p_rarch->video_driver_cache_context_ack = false;
- video_driver_reinit_context(p_rarch, flags);
- p_rarch->video_driver_cache_context = false;
- }
- bool video_driver_is_hw_context(void)
- {
- bool is_hw_context = false;
- struct rarch_state *p_rarch = &rarch_st;
- VIDEO_DRIVER_CONTEXT_LOCK();
- is_hw_context = (p_rarch->hw_render.context_type
- != RETRO_HW_CONTEXT_NONE);
- VIDEO_DRIVER_CONTEXT_UNLOCK();
- return is_hw_context;
- }
- const struct retro_hw_render_context_negotiation_interface *
- video_driver_get_context_negotiation_interface(void)
- {
- struct rarch_state *p_rarch = &rarch_st;
- return p_rarch->hw_render_context_negotiation;
- }
- bool video_driver_is_video_cache_context(void)
- {
- struct rarch_state *p_rarch = &rarch_st;
- return p_rarch->video_driver_cache_context;
- }
- void video_driver_set_video_cache_context_ack(void)
- {
- struct rarch_state *p_rarch = &rarch_st;
- p_rarch->video_driver_cache_context_ack = true;
- }
- bool video_driver_get_viewport_info(struct video_viewport *viewport)
- {
- struct rarch_state *p_rarch = &rarch_st;
- if (!p_rarch->current_video || !p_rarch->current_video->viewport_info)
- return false;
- p_rarch->current_video->viewport_info(
- p_rarch->video_driver_data, viewport);
- return true;
- }
- /**
- * video_viewport_get_scaled_integer:
- * @vp : Viewport handle
- * @width : Width.
- * @height : Height.
- * @aspect_ratio : Aspect ratio (in float).
- * @keep_aspect : Preserve aspect ratio?
- *
- * Gets viewport scaling dimensions based on
- * scaled integer aspect ratio.
- **/
- void video_viewport_get_scaled_integer(struct video_viewport *vp,
- unsigned width, unsigned height,
- float aspect_ratio, bool keep_aspect)
- {
- int padding_x = 0;
- int padding_y = 0;
- struct rarch_state *p_rarch = &rarch_st;
- settings_t *settings = p_rarch->configuration_settings;
- unsigned video_aspect_ratio_idx = settings->uints.video_aspect_ratio_idx;
- if (video_aspect_ratio_idx == ASPECT_RATIO_CUSTOM)
- {
- struct video_viewport *custom = video_viewport_get_custom();
- if (custom)
- {
- padding_x = width - custom->width;
- padding_y = height - custom->height;
- width = custom->width;
- height = custom->height;
- }
- }
- else
- {
- unsigned base_width;
- /* Use system reported sizes as these define the
- * geometry for the "normal" case. */
- unsigned base_height =
- p_rarch->video_driver_av_info.geometry.base_height;
- unsigned int rotation = retroarch_get_rotation();
- if (rotation % 2)
- base_height = p_rarch->video_driver_av_info.geometry.base_width;
- if (base_height == 0)
- base_height = 1;
- /* Account for non-square pixels.
- * This is sort of contradictory with the goal of integer scale,
- * but it is desirable in some cases.
- *
- * If square pixels are used, base_height will be equal to
- * system->av_info.base_height. */
- base_width = (unsigned)roundf(base_height * aspect_ratio);
- /* Make sure that we don't get 0x scale ... */
- if (width >= base_width && height >= base_height)
- {
- if (keep_aspect)
- {
- /* X/Y scale must be same. */
- unsigned max_scale = MIN(width / base_width,
- height / base_height);
- padding_x = width - base_width * max_scale;
- padding_y = height - base_height * max_scale;
- }
- else
- {
- /* X/Y can be independent, each scaled as much as possible. */
- padding_x = width % base_width;
- padding_y = height % base_height;
- }
- }
- width -= padding_x;
- height -= padding_y;
- }
- vp->width = width;
- vp->height = height;
- vp->x = padding_x / 2;
- vp->y = padding_y / 2;
- }
- struct video_viewport *video_viewport_get_custom(void)
- {
- struct rarch_state *p_rarch = &rarch_st;
- settings_t *settings = p_rarch->configuration_settings;
- return &settings->video_viewport_custom;
- }
- /**
- * video_driver_frame:
- * @data : pointer to data of the video frame.
- * @width : width of the video frame.
- * @height : height of the video frame.
- * @pitch : pitch of the video frame.
- *
- * Video frame render callback function.
- **/
- static void video_driver_frame(const void *data, unsigned width,
- unsigned height, size_t pitch)
- {
- char status_text[128];
- static char video_driver_msg[256];
- static retro_time_t curr_time;
- static retro_time_t fps_time;
- static float last_fps, frame_time;
- static uint64_t last_used_memory, last_total_memory;
- retro_time_t new_time;
- video_frame_info_t video_info;
- struct rarch_state *p_rarch = &rarch_st;
- const enum retro_pixel_format
- video_driver_pix_fmt = p_rarch->video_driver_pix_fmt;
- bool runloop_idle = p_rarch->runloop_idle;
- bool video_driver_active = p_rarch->video_driver_active;
- settings_t *settings = p_rarch->configuration_settings;
- #if defined(HAVE_GFX_WIDGETS)
- bool widgets_active = p_rarch->widgets_active;
- #endif
- status_text[0] = '\0';
- video_driver_msg[0] = '\0';
- if (!video_driver_active)
- return;
- new_time = cpu_features_get_time_usec();
- if (data)
- p_rarch->frame_cache_data = data;
- p_rarch->frame_cache_width = width;
- p_rarch->frame_cache_height = height;
- p_rarch->frame_cache_pitch = pitch;
- if (
- p_rarch->video_driver_scaler_ptr
- && data
- && (video_driver_pix_fmt == RETRO_PIXEL_FORMAT_0RGB1555)
- && (data != RETRO_HW_FRAME_BUFFER_VALID)
- && video_pixel_frame_scale(
- p_rarch->video_driver_scaler_ptr->scaler,
- p_rarch->video_driver_scaler_ptr->scaler_out,
- data, width, height, pitch)
- )
- {
- data = p_rarch->video_driver_scaler_ptr->scaler_out;
- pitch = p_rarch->video_driver_scaler_ptr->scaler->out_stride;
- }
- video_driver_build_info(&video_info);
- /* Get the amount of frames per seconds. */
- if (p_rarch->video_driver_frame_count)
- {
- unsigned fps_update_interval =
- settings->uints.fps_update_interval;
- unsigned memory_update_interval =
- settings->uints.memory_update_interval;
- size_t buf_pos = 1;
- /* set this to 1 to avoid an offset issue */
- unsigned write_index =
- p_rarch->video_driver_frame_time_count++ &
- (MEASURE_FRAME_TIME_SAMPLES_COUNT - 1);
- frame_time = new_time - fps_time;
- p_rarch->video_driver_frame_time_samples
- [write_index] = frame_time;
- fps_time = new_time;
- if (video_info.fps_show)
- buf_pos = snprintf(
- status_text, sizeof(status_text),
- "FPS: %6.2f", last_fps);
- if (video_info.framecount_show)
- {
- char frames_text[64];
- if (status_text[buf_pos-1] != '\0')
- strlcat(status_text, " || ", sizeof(status_text));
- snprintf(frames_text,
- sizeof(frames_text),
- "%s: %" PRIu64, msg_hash_to_str(MSG_FRAMES),
- (uint64_t)p_rarch->video_driver_frame_count);
- buf_pos = strlcat(status_text, frames_text, sizeof(status_text));
- }
- if (video_info.memory_show)
- {
- char mem[128];
- if ((p_rarch->video_driver_frame_count % memory_update_interval) == 0)
- {
- last_total_memory = frontend_driver_get_total_memory();
- last_used_memory = last_total_memory - frontend_driver_get_free_memory();
- }
- mem[0] = '\0';
- snprintf(
- mem, sizeof(mem), "MEM: %.2f/%.2fMB", last_used_memory / (1024.0f * 1024.0f),
- last_total_memory / (1024.0f * 1024.0f));
- if (status_text[buf_pos-1] != '\0')
- strlcat(status_text, " || ", sizeof(status_text));
- strlcat(status_text, mem, sizeof(status_text));
- }
- if ((p_rarch->video_driver_frame_count % fps_update_interval) == 0)
- {
- last_fps = TIME_TO_FPS(curr_time, new_time,
- fps_update_interval);
- strlcpy(p_rarch->video_driver_window_title,
- p_rarch->video_driver_title_buf,
- sizeof(p_rarch->video_driver_window_title));
- if (!string_is_empty(status_text))
- {
- strlcat(p_rarch->video_driver_window_title,
- " || ", sizeof(p_rarch->video_driver_window_title));
- strlcat(p_rarch->video_driver_window_title,
- status_text, sizeof(p_rarch->video_driver_window_title));
- }
- curr_time = new_time;
- p_rarch->video_driver_window_title_update = true;
- }
- }
- else
- {
- curr_time = fps_time = new_time;
- strlcpy(p_rarch->video_driver_window_title,
- p_rarch->video_driver_title_buf,
- sizeof(p_rarch->video_driver_window_title));
- if (video_info.fps_show)
- strlcpy(status_text,
- msg_hash_to_str(MENU_ENUM_LABEL_VALUE_NOT_AVAILABLE),
- sizeof(status_text));
- p_rarch->video_driver_window_title_update = true;
- }
- /* Add core status message to status text */
- if (video_info.core_status_msg_show)
- {
- /* Note: We need to lock a mutex here. Strictly
- * speaking, runloop_core_status_msg is not part
- * of the message queue, but:
- * - It may be implemented as a queue in the future
- * - It seems unnecessary to create a new slock_t
- * object for this type of message when
- * _runloop_msg_queue_lock is already available
- * We therefore just call runloop_msg_queue_lock()/
- * runloop_msg_queue_unlock() in this case */
- RUNLOOP_MSG_QUEUE_LOCK(p_rarch);
- /* Check whether duration timer has elapsed */
- runloop_core_status_msg.duration -= p_rarch->anim.delta_time;
- if (runloop_core_status_msg.duration < 0.0f)
- {
- runloop_core_status_msg.str[0] = '\0';
- runloop_core_status_msg.priority = 0;
- runloop_core_status_msg.duration = 0.0f;
- runloop_core_status_msg.set = false;
- }
- else
- {
- /* If status text is already set, add status
- * message at the end */
- if (!string_is_empty(status_text))
- {
- strlcat(status_text,
- " || ", sizeof(status_text));
- strlcat(status_text,
- runloop_core_status_msg.str, sizeof(status_text));
- }
- else
- strlcpy(status_text, runloop_core_status_msg.str,
- sizeof(status_text));
- }
- RUNLOOP_MSG_QUEUE_UNLOCK(p_rarch);
- }
- /* Slightly messy code,
- * but we really need to do processing before blocking on VSync
- * for best possible scheduling.
- */
- if (
- (
- #ifdef HAVE_VIDEO_FILTER
- !p_rarch->video_driver_state_filter ||
- #endif
- !video_info.post_filter_record
- || !data
- || p_rarch->video_driver_record_gpu_buffer
- ) && p_rarch->recording_data
- && p_rarch->recording_driver
- && p_rarch->recording_driver->push_video)
- recording_dump_frame(p_rarch,
- data, width, height,
- pitch, runloop_idle);
- #ifdef HAVE_VIDEO_FILTER
- if (data && p_rarch->video_driver_state_filter)
- {
- unsigned output_width = 0;
- unsigned output_height = 0;
- unsigned output_pitch = 0;
- rarch_softfilter_get_output_size(p_rarch->video_driver_state_filter,
- &output_width, &output_height, width, height);
- output_pitch = (output_width) * p_rarch->video_driver_state_out_bpp;
- rarch_softfilter_process(p_rarch->video_driver_state_filter,
- p_rarch->video_driver_state_buffer, output_pitch,
- data, width, height, pitch);
- if (video_info.post_filter_record
- && p_rarch->recording_data
- && p_rarch->recording_driver
- && p_rarch->recording_driver->push_video)
- recording_dump_frame(p_rarch,
- p_rarch->video_driver_state_buffer,
- output_width, output_height, output_pitch,
- runloop_idle);
- data = p_rarch->video_driver_state_buffer;
- width = output_width;
- height = output_height;
- pitch = output_pitch;
- }
- #endif
- if (p_rarch->runloop_msg_queue_size > 0)
- {
- /* If widgets are currently enabled, then
- * messages were pushed to the queue before
- * widgets were initialised - in this case, the
- * first item in the message queue should be
- * extracted and pushed to the widget message
- * queue instead */
- #if defined(HAVE_GFX_WIDGETS)
- if (widgets_active)
- {
- msg_queue_entry_t msg_entry;
- bool msg_found = false;
- RUNLOOP_MSG_QUEUE_LOCK(p_rarch);
- msg_found = msg_queue_extract(
- &p_rarch->runloop_msg_queue, &msg_entry);
- p_rarch->runloop_msg_queue_size = msg_queue_size(
- &p_rarch->runloop_msg_queue);
- RUNLOOP_MSG_QUEUE_UNLOCK(p_rarch);
- if (msg_found)
- gfx_widgets_msg_queue_push(
- &p_rarch->dispwidget_st,
- NULL,
- msg_entry.msg,
- roundf((float)msg_entry.duration / 60.0f * 1000.0f),
- msg_entry.title,
- msg_entry.icon,
- msg_entry.category,
- msg_entry.prio,
- false,
- #ifdef HAVE_MENU
- p_rarch->menu_driver_alive
- #else
- false
- #endif
- );
- }
- /* ...otherwise, just output message via
- * regular OSD notification text (if enabled) */
- else if (video_info.font_enable)
- #else
- if (video_info.font_enable)
- #endif
- {
- const char *msg = NULL;
- RUNLOOP_MSG_QUEUE_LOCK(p_rarch);
- msg = msg_queue_pull(&p_rarch->runloop_msg_queue);
- p_rarch->runloop_msg_queue_size = msg_queue_size(&p_rarch->runloop_msg_queue);
- if (msg)
- strlcpy(video_driver_msg, msg, sizeof(video_driver_msg));
- RUNLOOP_MSG_QUEUE_UNLOCK(p_rarch);
- }
- }
- if (video_info.statistics_show)
- {
- audio_statistics_t audio_stats;
- double stddev = 0.0;
- struct retro_system_av_info *av_info = &p_rarch->video_driver_av_info;
- unsigned red = 255;
- unsigned green = 255;
- unsigned blue = 255;
- unsigned alpha = 255;
- audio_stats.samples = 0;
- audio_stats.average_buffer_saturation = 0.0f;
- audio_stats.std_deviation_percentage = 0.0f;
- audio_stats.close_to_underrun = 0.0f;
- audio_stats.close_to_blocking = 0.0f;
- video_monitor_fps_statistics(NULL, &stddev, NULL);
- video_info.osd_stat_params.x = 0.010f;
- video_info.osd_stat_params.y = 0.950f;
- video_info.osd_stat_params.scale = 1.0f;
- video_info.osd_stat_params.full_screen = true;
- video_info.osd_stat_params.drop_x = -2;
- video_info.osd_stat_params.drop_y = -2;
- video_info.osd_stat_params.drop_mod = 0.3f;
- video_info.osd_stat_params.drop_alpha = 1.0f;
- video_info.osd_stat_params.color = COLOR_ABGR(
- red, green, blue, alpha);
- audio_compute_buffer_statistics(p_rarch, &audio_stats);
- snprintf(video_info.stat_text,
- sizeof(video_info.stat_text),
- "Video Statistics:\n -Frame rate: %6.2f fps\n -Frame time: %6.2f ms\n -Frame time deviation: %.3f %%\n"
- " -Frame count: %" PRIu64"\n -Viewport: %d x %d x %3.2f\n"
- "Audio Statistics:\n -Average buffer saturation: %.2f %%\n -Standard deviation: %.2f %%\n -Time spent close to underrun: %.2f %%\n -Time spent close to blocking: %.2f %%\n -Sample count: %d\n"
- "Core Geometry:\n -Size: %u x %u\n -Max Size: %u x %u\n -Aspect: %3.2f\nCore Timing:\n -FPS: %3.2f\n -Sample Rate: %6.2f\n",
- last_fps,
- frame_time / 1000.0f,
- 100.0f * stddev,
- p_rarch->video_driver_frame_count,
- video_info.width,
- video_info.height,
- video_info.refresh_rate,
- audio_stats.average_buffer_saturation,
- audio_stats.std_deviation_percentage,
- audio_stats.close_to_underrun,
- audio_stats.close_to_blocking,
- audio_stats.samples,
- av_info->geometry.base_width,
- av_info->geometry.base_height,
- av_info->geometry.max_width,
- av_info->geometry.max_height,
- av_info->geometry.aspect_ratio,
- av_info->timing.fps,
- av_info->timing.sample_rate);
- /* TODO/FIXME - add OSD chat text here */
- }
- if (p_rarch->current_video && p_rarch->current_video->frame)
- p_rarch->video_driver_active = p_rarch->current_video->frame(
- p_rarch->video_driver_data, data, width, height,
- p_rarch->video_driver_frame_count,
- (unsigned)pitch, video_driver_msg, &video_info);
- p_rarch->video_driver_frame_count++;
- /* Display the status text, with a higher priority. */
- if ( video_info.fps_show
- || video_info.framecount_show
- || video_info.memory_show
- || video_info.core_status_msg_show
- )
- {
- #if defined(HAVE_GFX_WIDGETS)
- if (widgets_active)
- strlcpy(
- p_rarch->dispwidget_st.gfx_widgets_status_text,
- status_text,
- sizeof(p_rarch->dispwidget_st.gfx_widgets_status_text)
- );
- else
- #endif
- {
- runloop_msg_queue_push(status_text, 2, 1, true, NULL,
- MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO);
- }
- }
- /* trigger set resolution*/
- if (video_info.crt_switch_resolution)
- {
- p_rarch->video_driver_crt_switching_active = true;
- switch (video_info.crt_switch_resolution_super)
- {
- case 2560:
- case 3840:
- case 1920:
- width =
- video_info.crt_switch_resolution_super;
- p_rarch->video_driver_crt_dynamic_super_width = false;
- break;
- case 1:
- p_rarch->video_driver_crt_dynamic_super_width = true;
- break;
- default:
- p_rarch->video_driver_crt_dynamic_super_width = false;
- break;
- }
- crt_switch_res_core(
- &p_rarch->crt_switch_st,
- width,
- height,
- p_rarch->video_driver_core_hz,
- video_info.crt_switch_resolution,
- video_info.crt_switch_center_adjust,
- video_info.crt_switch_porch_adjust,
- video_info.monitor_index,
- p_rarch->video_driver_crt_dynamic_super_width);
- }
- else if (!video_info.crt_switch_resolution)
- p_rarch->video_driver_crt_switching_active = false;
- }
- void crt_switch_driver_reinit(void)
- {
- video_driver_reinit(DRIVERS_CMD_ALL);
- }
- void video_driver_display_type_set(enum rarch_display_type type)
- {
- struct rarch_state *p_rarch = &rarch_st;
- p_rarch->video_driver_display_type = type;
- }
- uintptr_t video_driver_display_get(void)
- {
- struct rarch_state *p_rarch = &rarch_st;
- return p_rarch->video_driver_display;
- }
- uintptr_t video_driver_display_userdata_get(void)
- {
- struct rarch_state *p_rarch = &rarch_st;
- return p_rarch->video_driver_display_userdata;
- }
- void video_driver_display_userdata_set(uintptr_t idx)
- {
- struct rarch_state *p_rarch = &rarch_st;
- p_rarch->video_driver_display_userdata = idx;
- }
- void video_driver_display_set(uintptr_t idx)
- {
- struct rarch_state *p_rarch = &rarch_st;
- p_rarch->video_driver_display = idx;
- }
- enum rarch_display_type video_driver_display_type_get(void)
- {
- struct rarch_state *p_rarch = &rarch_st;
- return p_rarch->video_driver_display_type;
- }
- void video_driver_window_set(uintptr_t idx)
- {
- struct rarch_state *p_rarch = &rarch_st;
- p_rarch->video_driver_window = idx;
- }
- uintptr_t video_driver_window_get(void)
- {
- struct rarch_state *p_rarch = &rarch_st;
- return p_rarch->video_driver_window;
- }
- bool video_driver_texture_load(void *data,
- enum texture_filter_type filter_type,
- uintptr_t *id)
- {
- struct rarch_state *p_rarch = &rarch_st;
- if ( !id
- || !p_rarch->video_driver_poke
- || !p_rarch->video_driver_poke->load_texture)
- return false;
- *id = p_rarch->video_driver_poke->load_texture(
- p_rarch->video_driver_data, data,
- VIDEO_DRIVER_IS_THREADED_INTERNAL(),
- filter_type);
- return true;
- }
- bool video_driver_texture_unload(uintptr_t *id)
- {
- struct rarch_state *p_rarch = &rarch_st;
- if ( !p_rarch->video_driver_poke
- || !p_rarch->video_driver_poke->unload_texture)
- return false;
- p_rarch->video_driver_poke->unload_texture(
- p_rarch->video_driver_data,
- VIDEO_DRIVER_IS_THREADED_INTERNAL(),
- *id);
- *id = 0;
- return true;
- }
- void video_driver_build_info(video_frame_info_t *video_info)
- {
- video_viewport_t *custom_vp = NULL;
- struct rarch_state *p_rarch = &rarch_st;
- settings_t *settings = p_rarch->configuration_settings;
- #ifdef HAVE_THREADS
- bool is_threaded =
- VIDEO_DRIVER_IS_THREADED_INTERNAL();
- VIDEO_DRIVER_THREADED_LOCK(is_threaded);
- #endif
- custom_vp = &settings->video_viewport_custom;
- #ifdef HAVE_GFX_WIDGETS
- video_info->widgets_active = p_rarch->widgets_active;
- #else
- video_info->widgets_active = false;
- #endif
- video_info->refresh_rate = settings->floats.video_refresh_rate;
- video_info->crt_switch_resolution = settings->uints.crt_switch_resolution;
- video_info->crt_switch_resolution_super = settings->uints.crt_switch_resolution_super;
- video_info->crt_switch_center_adjust = settings->ints.crt_switch_center_adjust;
- video_info->crt_switch_porch_adjust = settings->ints.crt_switch_porch_adjust;
- video_info->black_frame_insertion = settings->uints.video_black_frame_insertion;
- video_info->hard_sync = settings->bools.video_hard_sync;
- video_info->hard_sync_frames = settings->uints.video_hard_sync_frames;
- video_info->fps_show = settings->bools.video_fps_show;
- video_info->memory_show = settings->bools.video_memory_show;
- video_info->statistics_show = settings->bools.video_statistics_show;
- video_info->framecount_show = settings->bools.video_framecount_show;
- video_info->core_status_msg_show = runloop_core_status_msg.set;
- video_info->aspect_ratio_idx = settings->uints.video_aspect_ratio_idx;
- video_info->post_filter_record = settings->bools.video_post_filter_record;
- video_info->input_menu_swap_ok_cancel_buttons = settings->bools.input_menu_swap_ok_cancel_buttons;
- video_info->max_swapchain_images = settings->uints.video_max_swapchain_images;
- video_info->windowed_fullscreen = settings->bools.video_windowed_fullscreen;
- video_info->fullscreen = settings->bools.video_fullscreen
- || p_rarch->rarch_force_fullscreen;
- video_info->menu_mouse_enable = settings->bools.menu_mouse_enable;
- video_info->monitor_index = settings->uints.video_monitor_index;
- video_info->font_enable = settings->bools.video_font_enable;
- video_info->font_msg_pos_x = settings->floats.video_msg_pos_x;
- video_info->font_msg_pos_y = settings->floats.video_msg_pos_y;
- video_info->font_msg_color_r = settings->floats.video_msg_color_r;
- video_info->font_msg_color_g = settings->floats.video_msg_color_g;
- video_info->font_msg_color_b = settings->floats.video_msg_color_b;
- video_info->custom_vp_x = custom_vp->x;
- video_info->custom_vp_y = custom_vp->y;
- video_info->custom_vp_width = custom_vp->width;
- video_info->custom_vp_height = custom_vp->height;
- video_info->custom_vp_full_width = custom_vp->full_width;
- video_info->custom_vp_full_height = custom_vp->full_height;
- #if defined(HAVE_GFX_WIDGETS)
- video_info->widgets_is_paused = p_rarch->gfx_widgets_paused;
- video_info->widgets_is_fast_forwarding = p_rarch->gfx_widgets_fast_forward;
- video_info->widgets_is_rewinding = p_rarch->gfx_widgets_rewinding;
- #else
- video_info->widgets_is_paused = false;
- video_info->widgets_is_fast_forwarding = false;
- video_info->widgets_is_rewinding = false;
- #endif
- video_info->width = p_rarch->video_driver_width;
- video_info->height = p_rarch->video_driver_height;
- video_info->use_rgba = p_rarch->video_driver_use_rgba;
- video_info->libretro_running = false;
- video_info->msg_bgcolor_enable =
- settings->bools.video_msg_bgcolor_enable;
- #ifdef HAVE_MENU
- video_info->menu_is_alive = p_rarch->menu_driver_alive;
- video_info->menu_footer_opacity = settings->floats.menu_footer_opacity;
- video_info->menu_header_opacity = settings->floats.menu_header_opacity;
- video_info->materialui_color_theme = settings->uints.menu_materialui_color_theme;
- video_info->ozone_color_theme = settings->uints.menu_ozone_color_theme;
- video_info->menu_shader_pipeline = settings->uints.menu_xmb_shader_pipeline;
- video_info->xmb_theme = settings->uints.menu_xmb_theme;
- video_info->xmb_color_theme = settings->uints.menu_xmb_color_theme;
- video_info->timedate_enable = settings->bools.menu_timedate_enable;
- video_info->battery_level_enable = settings->bools.menu_battery_level_enable;
- video_info->xmb_shadows_enable =
- settings->bools.menu_xmb_shadows_enable;
- video_info->xmb_alpha_factor =
- settings->uints.menu_xmb_alpha_factor;
- video_info->menu_wallpaper_opacity =
- settings->floats.menu_wallpaper_opacity;
- video_info->menu_framebuffer_opacity =
- settings->floats.menu_framebuffer_opacity;
- video_info->libretro_running = p_rarch->current_core.game_loaded;
- #else
- video_info->menu_is_alive = false;
- video_info->menu_footer_opacity = 0.0f;
- video_info->menu_header_opacity = 0.0f;
- video_info->materialui_color_theme = 0;
- video_info->menu_shader_pipeline = 0;
- video_info->xmb_color_theme = 0;
- video_info->xmb_theme = 0;
- video_info->timedate_enable = false;
- video_info->battery_level_enable = false;
- video_info->xmb_shadows_enable = false;
- video_info->xmb_alpha_factor = 0.0f;
- video_info->menu_framebuffer_opacity = 0.0f;
- video_info->menu_wallpaper_opacity = 0.0f;
- #endif
- video_info->runloop_is_paused = p_rarch->runloop_paused;
- video_info->runloop_is_slowmotion = p_rarch->runloop_slowmotion;
- video_info->input_driver_nonblock_state = p_rarch->input_driver_nonblock_state;
- video_info->input_driver_grab_mouse_state = p_rarch->input_driver_grab_mouse_state;
- video_info->userdata = VIDEO_DRIVER_GET_PTR_INTERNAL(p_rarch, false);
- #ifdef HAVE_THREADS
- VIDEO_DRIVER_THREADED_UNLOCK(is_threaded);
- #endif
- }
- /**
- * video_driver_translate_coord_viewport:
- * @mouse_x : Pointer X coordinate.
- * @mouse_y : Pointer Y coordinate.
- * @res_x : Scaled X coordinate.
- * @res_y : Scaled Y coordinate.
- * @res_screen_x : Scaled screen X coordinate.
- * @res_screen_y : Scaled screen Y coordinate.
- *
- * Translates pointer [X,Y] coordinates into scaled screen
- * coordinates based on viewport info.
- *
- * Returns: true (1) if successful, false if video driver doesn't support
- * viewport info.
- **/
- bool video_driver_translate_coord_viewport(
- struct video_viewport *vp,
- int mouse_x, int mouse_y,
- int16_t *res_x, int16_t *res_y,
- int16_t *res_screen_x, int16_t *res_screen_y)
- {
- int scaled_screen_x, scaled_screen_y, scaled_x, scaled_y;
- int norm_vp_width = (int)vp->width;
- int norm_vp_height = (int)vp->height;
- int norm_full_vp_width = (int)vp->full_width;
- int norm_full_vp_height = (int)vp->full_height;
- if (norm_vp_width <= 0 ||
- norm_vp_height <= 0 ||
- norm_full_vp_width <= 0 ||
- norm_full_vp_height <= 0)
- return false;
- if (mouse_x >= 0 && mouse_x <= norm_full_vp_width)
- scaled_screen_x = ((2 * mouse_x * 0x7fff)
- / norm_full_vp_width) - 0x7fff;
- else
- scaled_screen_x = -0x8000; /* OOB */
- if (mouse_y >= 0 && mouse_y <= norm_full_vp_height)
- scaled_screen_y = ((2 * mouse_y * 0x7fff)
- / norm_full_vp_height) - 0x7fff;
- else
- scaled_screen_y = -0x8000; /* OOB */
- mouse_x -= vp->x;
- mouse_y -= vp->y;
- if (mouse_x >= 0 && mouse_x <= norm_vp_width)
- scaled_x = ((2 * mouse_x * 0x7fff)
- / norm_vp_width) - 0x7fff;
- else
- scaled_x = -0x8000; /* OOB */
- if (mouse_y >= 0 && mouse_y <= norm_vp_height)
- scaled_y = ((2 * mouse_y * 0x7fff)
- / norm_vp_height) - 0x7fff;
- else
- scaled_y = -0x8000; /* OOB */
- *res_x = scaled_x;
- *res_y = scaled_y;
- *res_screen_x = scaled_screen_x;
- *res_screen_y = scaled_screen_y;
- return true;
- }
- bool video_driver_has_focus(void)
- {
- struct rarch_state *p_rarch = &rarch_st;
- return VIDEO_HAS_FOCUS(p_rarch);
- }
- void video_driver_get_window_title(char *buf, unsigned len)
- {
- struct rarch_state *p_rarch = &rarch_st;
- if (buf && p_rarch->video_driver_window_title_update)
- {
- struct rarch_state *p_rarch = &rarch_st;
- strlcpy(buf, p_rarch->video_driver_window_title, len);
- p_rarch->video_driver_window_title_update = false;
- }
- }
- /**
- * video_context_driver_init:
- * @data : Input data.
- * @ctx : Graphics context driver to initialize.
- * @ident : Identifier of graphics context driver to find.
- * @api : API of higher-level graphics API.
- * @major : Major version number of higher-level graphics API.
- * @minor : Minor version number of higher-level graphics API.
- * @hw_render_ctx : Request a graphics context driver capable of
- * hardware rendering?
- *
- * Initialize graphics context driver.
- *
- * Returns: graphics context driver if successfully initialized,
- * otherwise NULL.
- **/
- static const gfx_ctx_driver_t *video_context_driver_init(
- void *data,
- const gfx_ctx_driver_t *ctx,
- const char *ident,
- enum gfx_ctx_api api, unsigned major,
- unsigned minor, bool hw_render_ctx,
- void **ctx_data)
- {
- struct rarch_state *p_rarch = &rarch_st;
- settings_t *settings = p_rarch->configuration_settings;
- bool video_shared_context = settings->bools.video_shared_context || libretro_get_shared_context();
- if (!ctx->bind_api(data, api, major, minor))
- {
- RARCH_WARN("Failed to bind API (#%u, version %u.%u)"
- " on context driver \"%s\".\n",
- (unsigned)api, major, minor, ctx->ident);
- return NULL;
- }
- if (!(*ctx_data = ctx->init(data)))
- return NULL;
- if (ctx->bind_hw_render)
- ctx->bind_hw_render(*ctx_data,
- video_shared_context && hw_render_ctx);
- return ctx;
- }
- #ifdef HAVE_VULKAN
- static const gfx_ctx_driver_t *vk_context_driver_init_first(void *data,
- const char *ident, enum gfx_ctx_api api, unsigned major,
- unsigned minor, bool hw_render_ctx, void **ctx_data)
- {
- unsigned j;
- struct rarch_state *p_rarch = &rarch_st;
- int i = -1;
-
- for (j = 0; gfx_ctx_vk_drivers[j]; j++)
- {
- if (string_is_equal_noncase(ident, gfx_ctx_vk_drivers[j]->ident))
- {
- i = j;
- break;
- }
- }
- if (i >= 0)
- {
- const gfx_ctx_driver_t *ctx = video_context_driver_init(data,
- gfx_ctx_vk_drivers[i], ident,
- api, major, minor, hw_render_ctx, ctx_data);
- if (ctx)
- {
- p_rarch->video_context_data = *ctx_data;
- return ctx;
- }
- }
- for (i = 0; gfx_ctx_vk_drivers[i]; i++)
- {
- const gfx_ctx_driver_t *ctx =
- video_context_driver_init(data, gfx_ctx_vk_drivers[i], ident,
- api, major, minor, hw_render_ctx, ctx_data);
- if (ctx)
- {
- p_rarch->video_context_data = *ctx_data;
- return ctx;
- }
- }
- return NULL;
- }
- #endif
- static const gfx_ctx_driver_t *gl_context_driver_init_first(void *data,
- const char *ident, enum gfx_ctx_api api, unsigned major,
- unsigned minor, bool hw_render_ctx, void **ctx_data)
- {
- unsigned j;
- struct rarch_state *p_rarch = &rarch_st;
- int i = -1;
-
- for (j = 0; gfx_ctx_gl_drivers[j]; j++)
- {
- if (string_is_equal_noncase(ident, gfx_ctx_gl_drivers[j]->ident))
- {
- i = j;
- break;
- }
- }
- if (i >= 0)
- {
- const gfx_ctx_driver_t *ctx = video_context_driver_init(data,
- gfx_ctx_gl_drivers[i], ident,
- api, major, minor, hw_render_ctx, ctx_data);
- if (ctx)
- {
- p_rarch->video_context_data = *ctx_data;
- return ctx;
- }
- }
- for (i = 0; gfx_ctx_gl_drivers[i]; i++)
- {
- const gfx_ctx_driver_t *ctx =
- video_context_driver_init(data, gfx_ctx_gl_drivers[i], ident,
- api, major, minor, hw_render_ctx, ctx_data);
- if (ctx)
- {
- p_rarch->video_context_data = *ctx_data;
- return ctx;
- }
- }
- return NULL;
- }
- /**
- * video_context_driver_init_first:
- * @data : Input data.
- * @ident : Identifier of graphics context driver to find.
- * @api : API of higher-level graphics API.
- * @major : Major version number of higher-level graphics API.
- * @minor : Minor version number of higher-level graphics API.
- * @hw_render_ctx : Request a graphics context driver capable of
- * hardware rendering?
- *
- * Finds first suitable graphics context driver and initializes.
- *
- * Returns: graphics context driver if found, otherwise NULL.
- **/
- const gfx_ctx_driver_t *video_context_driver_init_first(void *data,
- const char *ident, enum gfx_ctx_api api, unsigned major,
- unsigned minor, bool hw_render_ctx, void **ctx_data)
- {
- switch (api)
- {
- case GFX_CTX_VULKAN_API:
- #ifdef HAVE_VULKAN
- {
- const gfx_ctx_driver_t *ptr = vk_context_driver_init_first(
- data, ident, api, major, minor, hw_render_ctx, ctx_data);
- if (ptr && !string_is_equal(ptr->ident, "null"))
- return ptr;
- /* fall-through if no valid driver was found */
- }
- #endif
- case GFX_CTX_OPENGL_API:
- case GFX_CTX_OPENGL_ES_API:
- case GFX_CTX_OPENVG_API:
- case GFX_CTX_METAL_API:
- return gl_context_driver_init_first(data, ident, api, major, minor,
- hw_render_ctx, ctx_data);
- case GFX_CTX_NONE:
- default:
- break;
- }
- return NULL;
- }
- void video_context_driver_free(void)
- {
- struct rarch_state *p_rarch = &rarch_st;
- video_context_driver_destroy_internal(&p_rarch->current_video_context);
- p_rarch->video_context_data = NULL;
- }
- bool video_context_driver_get_metrics(gfx_ctx_metrics_t *metrics)
- {
- struct rarch_state *p_rarch = &rarch_st;
- if (p_rarch && p_rarch->current_video_context.get_metrics)
- return p_rarch->current_video_context.get_metrics(
- p_rarch->video_context_data,
- metrics->type,
- metrics->value);
- return false;
- }
- bool video_context_driver_get_refresh_rate(float *refresh_rate)
- {
- struct rarch_state *p_rarch = &rarch_st;
- if (!p_rarch->current_video_context.get_refresh_rate || !refresh_rate)
- return false;
- if (!p_rarch->video_context_data)
- return false;
- if (!p_rarch->video_driver_crt_switching_active)
- {
- if (refresh_rate)
- *refresh_rate =
- p_rarch->current_video_context.get_refresh_rate(
- p_rarch->video_context_data);
- }
- else
- {
- float refresh_holder = 0;
- if (refresh_rate)
- refresh_holder =
- p_rarch->current_video_context.get_refresh_rate(
- p_rarch->video_context_data);
- /* Fix for incorrect interlacing detection --
- * HARD SET VSNC TO REQUIRED REFRESH FOR CRT*/
- if (refresh_holder != p_rarch->video_driver_core_hz)
- *refresh_rate = p_rarch->video_driver_core_hz;
- }
- return true;
- }
- bool video_context_driver_get_ident(gfx_ctx_ident_t *ident)
- {
- struct rarch_state *p_rarch = &rarch_st;
- if (!ident)
- return false;
- ident->ident = p_rarch->current_video_context.ident;
- return true;
- }
- bool video_context_driver_get_flags(gfx_ctx_flags_t *flags)
- {
- struct rarch_state *p_rarch = &rarch_st;
- if (!p_rarch->current_video_context.get_flags)
- return false;
- if (p_rarch->deferred_video_context_driver_set_flags)
- {
- flags->flags =
- p_rarch->deferred_flag_data.flags;
- p_rarch->deferred_video_context_driver_set_flags = false;
- return true;
- }
- flags->flags = p_rarch->current_video_context.get_flags(
- p_rarch->video_context_data);
- return true;
- }
- static bool video_driver_get_flags(gfx_ctx_flags_t *flags)
- {
- struct rarch_state *p_rarch = &rarch_st;
- if (!p_rarch->video_driver_poke || !p_rarch->video_driver_poke->get_flags)
- return false;
- flags->flags = p_rarch->video_driver_poke->get_flags(p_rarch->video_driver_data);
- return true;
- }
- gfx_ctx_flags_t video_driver_get_flags_wrapper(void)
- {
- gfx_ctx_flags_t flags;
- flags.flags = 0;
- if (!video_driver_get_flags(&flags))
- video_context_driver_get_flags(&flags);
- return flags;
- }
- /**
- * video_driver_test_all_flags:
- * @testflag : flag to test
- *
- * Poll both the video and context driver's flags and test
- * whether @testflag is set or not.
- **/
- bool video_driver_test_all_flags(enum display_flags testflag)
- {
- gfx_ctx_flags_t flags;
- if (video_driver_get_flags(&flags))
- if (BIT32_GET(flags.flags, testflag))
- return true;
- if (video_context_driver_get_flags(&flags))
- if (BIT32_GET(flags.flags, testflag))
- return true;
- return false;
- }
- bool video_context_driver_set_flags(gfx_ctx_flags_t *flags)
- {
- struct rarch_state *p_rarch = &rarch_st;
- if (!flags)
- return false;
- if (p_rarch->current_video_context.set_flags)
- {
- p_rarch->current_video_context.set_flags(
- p_rarch->video_context_data, flags->flags);
- return true;
- }
- p_rarch->deferred_flag_data.flags = flags->flags;
- p_rarch->deferred_video_context_driver_set_flags = true;
- return false;
- }
- enum gfx_ctx_api video_context_driver_get_api(void)
- {
- struct rarch_state *p_rarch = &rarch_st;
- enum gfx_ctx_api ctx_api = p_rarch->video_context_data ?
- p_rarch->current_video_context.get_api(
- p_rarch->video_context_data) : GFX_CTX_NONE;
- if (ctx_api == GFX_CTX_NONE)
- {
- const char *video_ident = (p_rarch->current_video)
- ? p_rarch->current_video->ident
- : NULL;
- if (string_starts_with_size(video_ident, "d3d", STRLEN_CONST("d3d")))
- {
- if (string_is_equal(video_ident, "d3d9"))
- return GFX_CTX_DIRECT3D9_API;
- else if (string_is_equal(video_ident, "d3d10"))
- return GFX_CTX_DIRECT3D10_API;
- else if (string_is_equal(video_ident, "d3d11"))
- return GFX_CTX_DIRECT3D11_API;
- else if (string_is_equal(video_ident, "d3d12"))
- return GFX_CTX_DIRECT3D12_API;
- }
- if (string_starts_with_size(video_ident, "gl", STRLEN_CONST("gl")))
- {
- if (string_is_equal(video_ident, "gl"))
- return GFX_CTX_OPENGL_API;
- else if (string_is_equal(video_ident, "gl1"))
- return GFX_CTX_OPENGL_API;
- else if (string_is_equal(video_ident, "glcore"))
- return GFX_CTX_OPENGL_API;
- }
- else if (string_is_equal(video_ident, "vulkan"))
- return GFX_CTX_VULKAN_API;
- else if (string_is_equal(video_ident, "metal"))
- return GFX_CTX_METAL_API;
- return GFX_CTX_NONE;
- }
- return ctx_api;
- }
- bool video_driver_has_windowed(void)
- {
- #if !(defined(RARCH_CONSOLE) || defined(RARCH_MOBILE))
- struct rarch_state *p_rarch = &rarch_st;
- if (p_rarch->video_driver_data && p_rarch->current_video->has_windowed)
- return p_rarch->current_video->has_windowed(p_rarch->video_driver_data);
- #endif
- return false;
- }
- bool video_driver_cached_frame_has_valid_framebuffer(void)
- {
- struct rarch_state *p_rarch = &rarch_st;
- if (p_rarch->frame_cache_data)
- return (p_rarch->frame_cache_data == RETRO_HW_FRAME_BUFFER_VALID);
- return false;
- }
- bool video_shader_driver_get_current_shader(video_shader_ctx_t *shader)
- {
- struct rarch_state *p_rarch = &rarch_st;
- void *video_driver = VIDEO_DRIVER_GET_PTR_INTERNAL(p_rarch, true);
- const video_poke_interface_t *video_poke = p_rarch->video_driver_poke;
- shader->data = NULL;
- if (!video_poke || !video_driver || !video_poke->get_current_shader)
- return false;
- shader->data = video_poke->get_current_shader(video_driver);
- return true;
- }
- float video_driver_get_refresh_rate(void)
- {
- struct rarch_state *p_rarch = &rarch_st;
- if (p_rarch->video_driver_poke && p_rarch->video_driver_poke->get_refresh_rate)
- return p_rarch->video_driver_poke->get_refresh_rate(p_rarch->video_driver_data);
- return 0.0f;
- }
- #if defined(HAVE_GFX_WIDGETS)
- bool video_driver_has_widgets(void)
- {
- struct rarch_state *p_rarch = &rarch_st;
- return p_rarch->current_video
- && p_rarch->current_video->gfx_widgets_enabled
- && p_rarch->current_video->gfx_widgets_enabled(
- p_rarch->video_driver_data);
- }
- #endif
- void video_driver_set_gpu_device_string(const char *str)
- {
- struct rarch_state *p_rarch = &rarch_st;
- strlcpy(p_rarch->video_driver_gpu_device_string, str,
- sizeof(p_rarch->video_driver_gpu_device_string));
- }
- const char* video_driver_get_gpu_device_string(void)
- {
- struct rarch_state *p_rarch = &rarch_st;
- return p_rarch->video_driver_gpu_device_string;
- }
- void video_driver_set_gpu_api_version_string(const char *str)
- {
- struct rarch_state *p_rarch = &rarch_st;
- strlcpy(p_rarch->video_driver_gpu_api_version_string, str,
- sizeof(p_rarch->video_driver_gpu_api_version_string));
- }
- const char* video_driver_get_gpu_api_version_string(void)
- {
- struct rarch_state *p_rarch = &rarch_st;
- return p_rarch->video_driver_gpu_api_version_string;
- }
- /* string list stays owned by the caller and must be available at
- * all times after the video driver is inited */
- void video_driver_set_gpu_api_devices(
- enum gfx_ctx_api api, struct string_list *list)
- {
- int i;
- for (i = 0; i < ARRAY_SIZE(gpu_map); i++)
- {
- if (api == gpu_map[i].api)
- {
- gpu_map[i].list = list;
- break;
- }
- }
- }
- struct string_list* video_driver_get_gpu_api_devices(enum gfx_ctx_api api)
- {
- int i;
- for (i = 0; i < ARRAY_SIZE(gpu_map); i++)
- {
- if (api == gpu_map[i].api)
- return gpu_map[i].list;
- }
- return NULL;
- }
- /* LOCATION */
- /**
- * config_get_location_driver_options:
- *
- * Get an enumerated list of all location driver names,
- * separated by '|'.
- *
- * Returns: string listing of all location driver names,
- * separated by '|'.
- **/
- const char* config_get_location_driver_options(void)
- {
- return char_list_new_special(STRING_LIST_LOCATION_DRIVERS, NULL);
- }
- static void find_location_driver(struct rarch_state *p_rarch)
- {
- settings_t *settings = p_rarch->configuration_settings;
- int i = (int)driver_find_index(
- "location_driver",
- settings->arrays.location_driver);
- if (i >= 0)
- p_rarch->location_driver = (const location_driver_t*)location_drivers[i];
- else
- {
- if (verbosity_is_enabled())
- {
- unsigned d;
- RARCH_ERR("Couldn't find any location driver named \"%s\"\n",
- settings->arrays.location_driver);
- RARCH_LOG_OUTPUT("Available location drivers are:\n");
- for (d = 0; location_drivers[d]; d++)
- RARCH_LOG_OUTPUT("\t%s\n", location_drivers[d]->ident);
- RARCH_WARN("Going to default to first location driver...\n");
- }
- p_rarch->location_driver = (const location_driver_t*)location_drivers[0];
- if (!p_rarch->location_driver)
- retroarch_fail(1, "find_location_driver()");
- }
- }
- /**
- * driver_location_start:
- *
- * Starts location driver interface..
- * Used by RETRO_ENVIRONMENT_GET_LOCATION_INTERFACE.
- *
- * Returns: true (1) if successful, otherwise false (0).
- **/
- static bool driver_location_start(void)
- {
- struct rarch_state *p_rarch = &rarch_st;
- if ( p_rarch->location_driver
- && p_rarch->location_data
- && p_rarch->location_driver->start)
- {
- settings_t *settings = p_rarch->configuration_settings;
- bool location_allow = settings->bools.location_allow;
- if (location_allow)
- return p_rarch->location_driver->start(p_rarch->location_data);
- runloop_msg_queue_push("Location is explicitly disabled.\n",
- 1, 180, true, NULL, MESSAGE_QUEUE_ICON_DEFAULT,
- MESSAGE_QUEUE_CATEGORY_INFO);
- }
- return false;
- }
- /**
- * driver_location_stop:
- *
- * Stops location driver interface..
- * Used by RETRO_ENVIRONMENT_GET_LOCATION_INTERFACE.
- *
- * Returns: true (1) if successful, otherwise false (0).
- **/
- static void driver_location_stop(void)
- {
- struct rarch_state *p_rarch = &rarch_st;
- if ( p_rarch->location_driver
- && p_rarch->location_driver->stop
- && p_rarch->location_data)
- p_rarch->location_driver->stop(p_rarch->location_data);
- }
- /**
- * driver_location_set_interval:
- * @interval_msecs : Interval time in milliseconds.
- * @interval_distance : Distance at which to update.
- *
- * Sets interval update time for location driver interface.
- * Used by RETRO_ENVIRONMENT_GET_LOCATION_INTERFACE.
- **/
- static void driver_location_set_interval(unsigned interval_msecs,
- unsigned interval_distance)
- {
- struct rarch_state *p_rarch = &rarch_st;
- if ( p_rarch->location_driver
- && p_rarch->location_driver->set_interval
- && p_rarch->location_data)
- p_rarch->location_driver->set_interval(p_rarch->location_data,
- interval_msecs, interval_distance);
- }
- /**
- * driver_location_get_position:
- * @lat : Latitude of current position.
- * @lon : Longitude of current position.
- * @horiz_accuracy : Horizontal accuracy.
- * @vert_accuracy : Vertical accuracy.
- *
- * Gets current positioning information from
- * location driver interface.
- * Used by RETRO_ENVIRONMENT_GET_LOCATION_INTERFACE.
- *
- * Returns: bool (1) if successful, otherwise false (0).
- **/
- static bool driver_location_get_position(double *lat, double *lon,
- double *horiz_accuracy, double *vert_accuracy)
- {
- struct rarch_state *p_rarch = &rarch_st;
- if ( p_rarch->location_driver
- && p_rarch->location_driver->get_position
- && p_rarch->location_data)
- return p_rarch->location_driver->get_position(p_rarch->location_data,
- lat, lon, horiz_accuracy, vert_accuracy);
- *lat = 0.0;
- *lon = 0.0;
- *horiz_accuracy = 0.0;
- *vert_accuracy = 0.0;
- return false;
- }
- static void init_location(void)
- {
- struct rarch_state *p_rarch = &rarch_st;
- rarch_system_info_t *system = &p_rarch->runloop_system;
- /* Resource leaks will follow if location interface is initialized twice. */
- if (p_rarch->location_data)
- return;
- find_location_driver(p_rarch);
- p_rarch->location_data = p_rarch->location_driver->init();
- if (!p_rarch->location_data)
- {
- RARCH_ERR("Failed to initialize location driver. Will continue without location.\n");
- p_rarch->location_driver_active = false;
- }
- if (system->location_cb.initialized)
- system->location_cb.initialized();
- }
- static void uninit_location(void)
- {
- struct rarch_state *p_rarch = &rarch_st;
- rarch_system_info_t *system = &p_rarch->runloop_system;
- if (p_rarch->location_data && p_rarch->location_driver)
- {
- if (system->location_cb.deinitialized)
- system->location_cb.deinitialized();
- if (p_rarch->location_driver->free)
- p_rarch->location_driver->free(p_rarch->location_data);
- }
- p_rarch->location_data = NULL;
- }
- /* CAMERA */
- /**
- * config_get_camera_driver_options:
- *
- * Get an enumerated list of all camera driver names,
- * separated by '|'.
- *
- * Returns: string listing of all camera driver names,
- * separated by '|'.
- **/
- const char *config_get_camera_driver_options(void)
- {
- return char_list_new_special(STRING_LIST_CAMERA_DRIVERS, NULL);
- }
- static bool driver_camera_start(void)
- {
- struct rarch_state *p_rarch = &rarch_st;
- if ( p_rarch->camera_driver &&
- p_rarch->camera_data &&
- p_rarch->camera_driver->start)
- {
- settings_t *settings = p_rarch->configuration_settings;
- bool camera_allow = settings->bools.camera_allow;
- if (camera_allow)
- return p_rarch->camera_driver->start(p_rarch->camera_data);
- runloop_msg_queue_push(
- "Camera is explicitly disabled.\n", 1, 180, false,
- NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO);
- }
- return true;
- }
- static void driver_camera_stop(void)
- {
- struct rarch_state *p_rarch = &rarch_st;
- if ( p_rarch->camera_driver
- && p_rarch->camera_driver->stop
- && p_rarch->camera_data)
- p_rarch->camera_driver->stop(p_rarch->camera_data);
- }
- static void camera_driver_find_driver(struct rarch_state *p_rarch)
- {
- settings_t *settings = p_rarch->configuration_settings;
- int i = (int)driver_find_index(
- "camera_driver",
- settings->arrays.camera_driver);
- if (i >= 0)
- p_rarch->camera_driver = (const camera_driver_t*)camera_drivers[i];
- else
- {
- if (verbosity_is_enabled())
- {
- unsigned d;
- RARCH_ERR("Couldn't find any camera driver named \"%s\"\n",
- settings->arrays.camera_driver);
- RARCH_LOG_OUTPUT("Available camera drivers are:\n");
- for (d = 0; camera_drivers[d]; d++)
- {
- if (camera_drivers[d])
- {
- RARCH_LOG_OUTPUT("\t%s\n", camera_drivers[d]->ident);
- }
- }
- RARCH_WARN("Going to default to first camera driver...\n");
- }
- p_rarch->camera_driver = (const camera_driver_t*)camera_drivers[0];
- if (!p_rarch->camera_driver)
- retroarch_fail(1, "find_camera_driver()");
- }
- }
- static void driver_adjust_system_rates(struct rarch_state *p_rarch)
- {
- settings_t *settings = p_rarch->configuration_settings;
- struct retro_system_av_info *av_info = &p_rarch->video_driver_av_info;
- const struct retro_system_timing *info =
- (const struct retro_system_timing*)&av_info->timing;
-
- if (info->sample_rate > 0.0)
- {
- p_rarch->audio_driver_input =
- audio_driver_monitor_adjust_system_rates(
- p_rarch->configuration_settings,
- &p_rarch->video_driver_av_info
- );
-
- RARCH_LOG("[Audio]: Set audio input rate to: %.2f Hz.\n",
- p_rarch->audio_driver_input);
- }
-
- p_rarch->runloop_force_nonblock = false;
- video_driver_monitor_adjust_system_rates(p_rarch);
- if (!VIDEO_DRIVER_GET_PTR_INTERNAL(p_rarch, false))
- return;
- if (p_rarch->runloop_force_nonblock)
- {
- bool video_adaptive_vsync = settings->bools.video_adaptive_vsync;
- unsigned video_swap_interval = settings->uints.video_swap_interval;
- if (p_rarch->current_video->set_nonblock_state)
- p_rarch->current_video->set_nonblock_state(
- p_rarch->video_driver_data, true,
- video_driver_test_all_flags(GFX_CTX_FLAGS_ADAPTIVE_VSYNC) &&
- video_adaptive_vsync,
- video_swap_interval
- );
- }
- else
- driver_set_nonblock_state();
- }
- /**
- * driver_set_nonblock_state:
- *
- * Sets audio and video drivers to nonblock state (if enabled).
- *
- * If nonblock state is false, sets
- * blocking state for both audio and video drivers instead.
- **/
- void driver_set_nonblock_state(void)
- {
- struct rarch_state *p_rarch = &rarch_st;
- bool enable = p_rarch->input_driver_nonblock_state;
- settings_t *settings = p_rarch->configuration_settings;
- bool audio_sync = settings->bools.audio_sync;
- bool video_vsync = settings->bools.video_vsync;
- bool adaptive_vsync = settings->bools.video_adaptive_vsync;
- unsigned swap_interval = settings->uints.video_swap_interval;
- bool video_driver_active = p_rarch->video_driver_active;
- bool audio_driver_active = p_rarch->audio_driver_active;
- bool runloop_force_nonblock = p_rarch->runloop_force_nonblock;
- /* Only apply non-block-state for video if we're using vsync. */
- if (video_driver_active && VIDEO_DRIVER_GET_PTR_INTERNAL(p_rarch, false))
- {
- if (p_rarch->current_video->set_nonblock_state)
- {
- bool video_nonblock = enable;
- if (!video_vsync || runloop_force_nonblock)
- video_nonblock = true;
- p_rarch->current_video->set_nonblock_state(p_rarch->video_driver_data,
- video_nonblock,
- video_driver_test_all_flags(GFX_CTX_FLAGS_ADAPTIVE_VSYNC) &&
- adaptive_vsync, swap_interval);
- }
- }
- if (audio_driver_active && p_rarch->audio_driver_context_audio_data)
- p_rarch->current_audio->set_nonblock_state(
- p_rarch->audio_driver_context_audio_data,
- audio_sync ? enable : true);
- p_rarch->audio_driver_chunk_size = enable
- ? p_rarch->audio_driver_chunk_nonblock_size
- : p_rarch->audio_driver_chunk_block_size;
- }
- /**
- * drivers_init:
- * @flags : Bitmask of drivers to initialize.
- *
- * Initializes drivers.
- * @flags determines which drivers get initialized.
- **/
- static void drivers_init(struct rarch_state *p_rarch, int flags)
- {
- #ifdef HAVE_MENU
- struct menu_state *menu_st = &p_rarch->menu_driver_state;
- #endif
- bool video_is_threaded = VIDEO_DRIVER_IS_THREADED_INTERNAL();
- settings_t *settings = p_rarch->configuration_settings;
- #if defined(HAVE_GFX_WIDGETS)
- bool video_font_enable = settings->bools.video_font_enable;
- bool menu_enable_widgets = settings->bools.menu_enable_widgets;
- /* By default, we want display widgets to persist through driver reinits. */
- p_rarch->widgets_persisting = true;
- #endif
- #ifdef HAVE_MENU
- /* By default, we want the menu to persist through driver reinits. */
- if (menu_st)
- menu_st->data_own = true;
- #endif
- if (flags & (DRIVER_VIDEO_MASK | DRIVER_AUDIO_MASK))
- driver_adjust_system_rates(p_rarch);
- /* Initialize video driver */
- if (flags & DRIVER_VIDEO_MASK)
- {
- struct retro_hw_render_callback *hwr =
- VIDEO_DRIVER_GET_HW_CONTEXT_INTERNAL(p_rarch);
- p_rarch->video_driver_frame_time_count = 0;
- video_driver_lock_new();
- #ifdef HAVE_VIDEO_FILTER
- video_driver_filter_free();
- #endif
- video_driver_set_cached_frame_ptr(NULL);
- video_driver_init_internal(&video_is_threaded);
- if (!p_rarch->video_driver_cache_context_ack
- && hwr->context_reset)
- hwr->context_reset();
- p_rarch->video_driver_cache_context_ack = false;
- p_rarch->runloop_frame_time_last = 0;
- }
- /* Initialize audio driver */
- if (flags & DRIVER_AUDIO_MASK)
- {
- audio_driver_init_internal(p_rarch,
- p_rarch->audio_callback.callback != NULL);
- if ( p_rarch->current_audio &&
- p_rarch->current_audio->device_list_new &&
- p_rarch->audio_driver_context_audio_data)
- p_rarch->audio_driver_devices_list = (struct string_list*)
- p_rarch->current_audio->device_list_new(
- p_rarch->audio_driver_context_audio_data);
- }
- if (flags & DRIVER_CAMERA_MASK)
- {
- /* Only initialize camera driver if we're ever going to use it. */
- if (p_rarch->camera_driver_active)
- {
- /* Resource leaks will follow if camera is initialized twice. */
- if (!p_rarch->camera_data)
- {
- camera_driver_find_driver(p_rarch);
- if (p_rarch->camera_driver)
- {
- p_rarch->camera_data = p_rarch->camera_driver->init(
- *settings->arrays.camera_device ?
- settings->arrays.camera_device : NULL,
- p_rarch->camera_cb.caps,
- settings->uints.camera_width ?
- settings->uints.camera_width : p_rarch->camera_cb.width,
- settings->uints.camera_height ?
- settings->uints.camera_height : p_rarch->camera_cb.height);
- if (!p_rarch->camera_data)
- {
- RARCH_ERR("Failed to initialize camera driver. Will continue without camera.\n");
- p_rarch->camera_driver_active = false;
- }
- if (p_rarch->camera_cb.initialized)
- p_rarch->camera_cb.initialized();
- }
- }
- }
- }
- if (flags & DRIVER_BLUETOOTH_MASK)
- bluetooth_driver_ctl(RARCH_BLUETOOTH_CTL_INIT, NULL);
- if ((flags & DRIVER_WIFI_MASK))
- wifi_driver_ctl(RARCH_WIFI_CTL_INIT, NULL);
- if (flags & DRIVER_LOCATION_MASK)
- {
- /* Only initialize location driver if we're ever going to use it. */
- if (p_rarch->location_driver_active)
- init_location();
- }
- core_info_init_current_core();
- #if defined(HAVE_GFX_WIDGETS)
- /* Note that we only enable widgets if 'video_font_enable'
- * is true. 'video_font_enable' corresponds to the generic
- * 'On-Screen Notifications' setting, which should serve as
- * a global notifications on/off toggle switch */
- if (video_font_enable &&
- menu_enable_widgets &&
- video_driver_has_widgets())
- {
- bool rarch_force_fullscreen = p_rarch->rarch_force_fullscreen;
- bool video_is_fullscreen = settings->bools.video_fullscreen ||
- rarch_force_fullscreen;
- p_rarch->widgets_active = gfx_widgets_init(
- (uintptr_t)&p_rarch->widgets_active,
- video_is_threaded,
- p_rarch->video_driver_width,
- p_rarch->video_driver_height,
- video_is_fullscreen,
- settings->paths.directory_assets,
- settings->paths.path_font);
- }
- else
- #endif
- {
- gfx_display_init_first_driver(video_is_threaded);
- }
- #ifdef HAVE_MENU
- if (flags & DRIVER_VIDEO_MASK)
- {
- /* Initialize menu driver */
- if (flags & DRIVER_MENU_MASK)
- {
- if (!menu_driver_init(video_is_threaded))
- RARCH_ERR("Unable to init menu driver.\n");
- #ifdef HAVE_LIBRETRODB
- menu_explore_context_init();
- #endif
- }
- }
- /* Initialising the menu driver will also initialise
- * core info - if we are not initialising the menu
- * driver, must initialise core info 'by hand' */
- if (!(flags & DRIVER_VIDEO_MASK) ||
- !(flags & DRIVER_MENU_MASK))
- {
- command_event(CMD_EVENT_CORE_INFO_INIT, NULL);
- command_event(CMD_EVENT_LOAD_CORE_PERSIST, NULL);
- }
- #else
- /* Qt uses core info, even if the menu is disabled */
- command_event(CMD_EVENT_CORE_INFO_INIT, NULL);
- command_event(CMD_EVENT_LOAD_CORE_PERSIST, NULL);
- #endif
- if (flags & (DRIVER_VIDEO_MASK | DRIVER_AUDIO_MASK))
- {
- /* Keep non-throttled state as good as possible. */
- if (p_rarch->input_driver_nonblock_state)
- driver_set_nonblock_state();
- }
- /* Initialize LED driver */
- if (flags & DRIVER_LED_MASK)
- led_driver_init(settings->arrays.led_driver);
- /* Initialize MIDI driver */
- if (flags & DRIVER_MIDI_MASK)
- midi_driver_init(p_rarch);
- }
- /**
- * Driver ownership - set this to true if the platform in
- * question needs to 'own'
- * the respective handle and therefore skip regular RetroArch
- * driver teardown/reiniting procedure.
- *
- * If to true, the 'free' function will get skipped. It is
- * then up to the driver implementation to properly handle
- * 'reiniting' inside the 'init' function and make sure it
- * returns the existing handle instead of allocating and
- * returning a pointer to a new handle.
- *
- * Typically, if a driver intends to make use of this, it should
- * set this to true at the end of its 'init' function.
- **/
- static void driver_uninit(struct rarch_state *p_rarch, int flags)
- {
- core_info_deinit_list();
- core_info_free_current_core(&p_rarch->core_info_st);
- #if defined(HAVE_GFX_WIDGETS)
- /* This absolutely has to be done before video_driver_free_internal()
- * is called/completes, otherwise certain menu drivers
- * (e.g. Vulkan) will segfault */
- if (gfx_widgets_deinit(p_rarch->widgets_persisting))
- p_rarch->widgets_active = false;
- #endif
- #ifdef HAVE_MENU
- if (flags & DRIVER_MENU_MASK)
- {
- #ifdef HAVE_LIBRETRODB
- menu_explore_context_deinit();
- #endif
- menu_driver_ctl(RARCH_MENU_CTL_DEINIT, NULL);
- }
- #endif
- if ((flags & DRIVER_LOCATION_MASK))
- uninit_location();
- if ((flags & DRIVER_CAMERA_MASK))
- {
- if (p_rarch->camera_data && p_rarch->camera_driver)
- {
- if (p_rarch->camera_cb.deinitialized)
- p_rarch->camera_cb.deinitialized();
- if (p_rarch->camera_driver->free)
- p_rarch->camera_driver->free(p_rarch->camera_data);
- }
- p_rarch->camera_data = NULL;
- }
- if ((flags & DRIVER_BLUETOOTH_MASK))
- bluetooth_driver_ctl(RARCH_BLUETOOTH_CTL_DEINIT, NULL);
- if ((flags & DRIVER_WIFI_MASK))
- wifi_driver_ctl(RARCH_WIFI_CTL_DEINIT, NULL);
- if (flags & DRIVER_LED)
- led_driver_free();
- if (flags & DRIVERS_VIDEO_INPUT)
- {
- video_driver_free_internal(p_rarch);
- VIDEO_DRIVER_LOCK_FREE();
- p_rarch->video_driver_data = NULL;
- video_driver_set_cached_frame_ptr(NULL);
- }
- if (flags & DRIVER_AUDIO_MASK)
- audio_driver_deinit(p_rarch);
- if ((flags & DRIVER_VIDEO_MASK))
- p_rarch->video_driver_data = NULL;
- if ((flags & DRIVER_INPUT_MASK))
- p_rarch->current_input_data = NULL;
- if ((flags & DRIVER_AUDIO_MASK))
- p_rarch->audio_driver_context_audio_data = NULL;
- if (flags & DRIVER_MIDI_MASK)
- midi_driver_free(p_rarch);
- }
- static void retroarch_deinit_drivers(struct rarch_state *p_rarch, struct retro_callbacks *cbs)
- {
- #if defined(HAVE_GFX_WIDGETS)
- /* Tear down display widgets no matter what
- * in case the handle is lost in the threaded
- * video driver in the meantime
- * (breaking video_driver_has_widgets) */
- if (gfx_widgets_deinit(p_rarch->widgets_persisting))
- p_rarch->widgets_active = false;
- #endif
- /* Video */
- video_display_server_destroy();
- p_rarch->video_driver_use_rgba = false;
- p_rarch->video_driver_active = false;
- p_rarch->video_driver_cache_context = false;
- p_rarch->video_driver_cache_context_ack = false;
- p_rarch->video_driver_record_gpu_buffer = NULL;
- p_rarch->current_video = NULL;
- video_driver_set_cached_frame_ptr(NULL);
- /* Audio */
- p_rarch->audio_driver_active = false;
- p_rarch->current_audio = NULL;
- /* Input */
- p_rarch->input_driver_keyboard_linefeed_enable = false;
- p_rarch->input_driver_block_hotkey = false;
- p_rarch->input_driver_block_libretro_input = false;
- p_rarch->input_driver_nonblock_state = false;
- p_rarch->input_driver_flushing_input = 0;
- memset(&p_rarch->input_driver_turbo_btns, 0, sizeof(turbo_buttons_t));
- p_rarch->current_input = NULL;
- #ifdef HAVE_MENU
- menu_driver_destroy(p_rarch,
- &p_rarch->menu_driver_state);
- p_rarch->menu_driver_alive = false;
- #endif
- p_rarch->location_driver_active = false;
- p_rarch->location_driver = NULL;
- /* Camera */
- p_rarch->camera_driver_active = false;
- p_rarch->camera_driver = NULL;
- p_rarch->camera_data = NULL;
- bluetooth_driver_ctl(RARCH_BLUETOOTH_CTL_DESTROY, NULL);
- wifi_driver_ctl(RARCH_WIFI_CTL_DESTROY, NULL);
- cbs->frame_cb = retro_frame_null;
- cbs->poll_cb = retro_input_poll_null;
- cbs->sample_cb = NULL;
- cbs->sample_batch_cb = NULL;
- cbs->state_cb = NULL;
- p_rarch->current_core.inited = false;
- }
- bool driver_ctl(enum driver_ctl_state state, void *data)
- {
- struct rarch_state *p_rarch = &rarch_st;
- driver_ctx_info_t *drv = (driver_ctx_info_t*)data;
- switch (state)
- {
- case RARCH_DRIVER_CTL_SET_REFRESH_RATE:
- {
- float *hz = (float*)data;
- video_monitor_set_refresh_rate(*hz);
- /* Sets audio monitor rate to new value. */
- p_rarch->audio_source_ratio_original =
- p_rarch->audio_source_ratio_current =
- (double)p_rarch->configuration_settings->uints.audio_out_rate
- / p_rarch->audio_driver_input;
- driver_adjust_system_rates(p_rarch);
- }
- break;
- case RARCH_DRIVER_CTL_FIND_FIRST:
- if (!drv)
- return false;
- find_driver_nonempty(drv->label, 0, drv->s, drv->len);
- break;
- case RARCH_DRIVER_CTL_FIND_LAST:
- if (!drv)
- return false;
- driver_find_last(drv->label, drv->s, drv->len);
- break;
- case RARCH_DRIVER_CTL_FIND_PREV:
- if (!drv)
- return false;
- return driver_find_prev(drv->label, drv->s, drv->len);
- case RARCH_DRIVER_CTL_FIND_NEXT:
- if (!drv)
- return false;
- return driver_find_next(drv->label, drv->s, drv->len);
- case RARCH_DRIVER_CTL_NONE:
- default:
- break;
- }
- return true;
- }
- /* RUNAHEAD */
- #ifdef HAVE_RUNAHEAD
- static void mylist_resize(my_list *list,
- int new_size, bool run_constructor)
- {
- int i;
- int new_capacity;
- int old_size;
- void *element = NULL;
- if (new_size < 0)
- new_size = 0;
- new_capacity = new_size;
- old_size = list->size;
- if (new_size == old_size)
- return;
- if (new_size > list->capacity)
- {
- if (new_capacity < list->capacity * 2)
- new_capacity = list->capacity * 2;
- /* try to realloc */
- list->data = (void**)realloc(
- (void*)list->data, new_capacity * sizeof(void*));
- for (i = list->capacity; i < new_capacity; i++)
- list->data[i] = NULL;
- list->capacity = new_capacity;
- }
- if (new_size <= list->size)
- {
- for (i = new_size; i < list->size; i++)
- {
- element = list->data[i];
- if (element)
- {
- list->destructor(element);
- list->data[i] = NULL;
- }
- }
- }
- else
- {
- for (i = list->size; i < new_size; i++)
- {
- list->data[i] = NULL;
- if (run_constructor)
- list->data[i] = list->constructor();
- }
- }
- list->size = new_size;
- }
- static void *mylist_add_element(my_list *list)
- {
- int old_size = list->size;
- if (list)
- mylist_resize(list, old_size + 1, true);
- return list->data[old_size];
- }
- static void mylist_destroy(my_list **list_p)
- {
- my_list *list = NULL;
- if (!list_p)
- return;
- list = *list_p;
- if (list)
- {
- mylist_resize(list, 0, false);
- free(list->data);
- free(list);
- *list_p = NULL;
- }
- }
- static void mylist_create(my_list **list_p, int initial_capacity,
- constructor_t constructor, destructor_t destructor)
- {
- my_list *list = NULL;
- if (!list_p)
- return;
- list = *list_p;
- if (list)
- mylist_destroy(list_p);
- list = (my_list*)malloc(sizeof(my_list));
- *list_p = list;
- list->size = 0;
- list->constructor = constructor;
- list->destructor = destructor;
- list->data = (void**)calloc(initial_capacity, sizeof(void*));
- list->capacity = initial_capacity;
- }
- static void *input_list_element_constructor(void)
- {
- void *ptr = malloc(sizeof(input_list_element));
- input_list_element *element = (input_list_element*)ptr;
- element->port = 0;
- element->device = 0;
- element->index = 0;
- element->state = (int16_t*)calloc(256, sizeof(int16_t));
- element->state_size = 256;
- return ptr;
- }
- static void input_list_element_realloc(
- input_list_element *element,
- unsigned int new_size)
- {
- if (new_size > element->state_size)
- {
- element->state = (int16_t*)realloc(element->state,
- new_size * sizeof(int16_t));
- memset(&element->state[element->state_size], 0,
- (new_size - element->state_size) * sizeof(int16_t));
- element->state_size = new_size;
- }
- }
- static void input_list_element_expand(
- input_list_element *element, unsigned int new_index)
- {
- unsigned int new_size = element->state_size;
- if (new_size == 0)
- new_size = 32;
- while (new_index >= new_size)
- new_size *= 2;
- input_list_element_realloc(element, new_size);
- }
- static void input_list_element_destructor(void* element_ptr)
- {
- input_list_element *element = (input_list_element*)element_ptr;
- if (!element)
- return;
- free(element->state);
- free(element_ptr);
- }
- static void input_state_set_last(unsigned port, unsigned device,
- unsigned index, unsigned id, int16_t value)
- {
- unsigned i;
- input_list_element *element = NULL;
- struct rarch_state *p_rarch = &rarch_st;
- if (!p_rarch->input_state_list)
- mylist_create(&p_rarch->input_state_list, 16,
- input_list_element_constructor,
- input_list_element_destructor);
- /* Find list item */
- for (i = 0; i < (unsigned)p_rarch->input_state_list->size; i++)
- {
- element = (input_list_element*)p_rarch->input_state_list->data[i];
- if ( (element->port == port) &&
- (element->device == device) &&
- (element->index == index)
- )
- {
- if (id >= element->state_size)
- input_list_element_expand(element, id);
- element->state[id] = value;
- return;
- }
- }
- element = NULL;
- if (p_rarch->input_state_list)
- element = (input_list_element*)
- mylist_add_element(p_rarch->input_state_list);
- element->port = port;
- element->device = device;
- element->index = index;
- if (id >= element->state_size)
- input_list_element_expand(element, id);
- element->state[id] = value;
- }
- static int16_t input_state_get_last(unsigned port,
- unsigned device, unsigned index, unsigned id)
- {
- unsigned i;
- struct rarch_state *p_rarch = &rarch_st;
- if (!p_rarch->input_state_list)
- return 0;
- /* find list item */
- for (i = 0; i < (unsigned)p_rarch->input_state_list->size; i++)
- {
- input_list_element *element =
- (input_list_element*)p_rarch->input_state_list->data[i];
- if ( (element->port == port) &&
- (element->device == device) &&
- (element->index == index))
- {
- if (id < element->state_size)
- return element->state[id];
- return 0;
- }
- }
- return 0;
- }
- static int16_t input_state_with_logging(unsigned port,
- unsigned device, unsigned index, unsigned id)
- {
- struct rarch_state *p_rarch = &rarch_st;
- if (p_rarch->input_state_callback_original)
- {
- int16_t result = p_rarch->input_state_callback_original(
- port, device, index, id);
- int16_t last_input =
- input_state_get_last(port, device, index, id);
- if (result != last_input)
- p_rarch->input_is_dirty = true;
- /*arbitrary limit of up to 65536 elements in state array*/
- if (id < 65536)
- input_state_set_last(port, device, index, id, result);
- return result;
- }
- return 0;
- }
- static void reset_hook(void)
- {
- struct rarch_state *p_rarch = &rarch_st;
- p_rarch->input_is_dirty = true;
- if (p_rarch->retro_reset_callback_original)
- p_rarch->retro_reset_callback_original();
- }
- static bool unserialize_hook(const void *buf, size_t size)
- {
- struct rarch_state *p_rarch = &rarch_st;
- p_rarch->input_is_dirty = true;
- if (p_rarch->retro_unserialize_callback_original)
- return p_rarch->retro_unserialize_callback_original(buf, size);
- return false;
- }
- static void add_input_state_hook(struct rarch_state *p_rarch)
- {
- struct retro_callbacks *cbs = &p_rarch->retro_ctx;
- if (!p_rarch->input_state_callback_original)
- {
- p_rarch->input_state_callback_original = cbs->state_cb;
- cbs->state_cb = input_state_with_logging;
- p_rarch->current_core.retro_set_input_state(cbs->state_cb);
- }
- if (!p_rarch->retro_reset_callback_original)
- {
- p_rarch->retro_reset_callback_original = p_rarch->current_core.retro_reset;
- p_rarch->current_core.retro_reset = reset_hook;
- }
- if (!p_rarch->retro_unserialize_callback_original)
- {
- p_rarch->retro_unserialize_callback_original = p_rarch->current_core.retro_unserialize;
- p_rarch->current_core.retro_unserialize = unserialize_hook;
- }
- }
- static void remove_input_state_hook(struct rarch_state *p_rarch)
- {
- struct retro_callbacks *cbs = &p_rarch->retro_ctx;
- if (p_rarch->input_state_callback_original)
- {
- cbs->state_cb = p_rarch->input_state_callback_original;
- p_rarch->current_core.retro_set_input_state(cbs->state_cb);
- p_rarch->input_state_callback_original = NULL;
- mylist_destroy(&p_rarch->input_state_list);
- }
- if (p_rarch->retro_reset_callback_original)
- {
- p_rarch->current_core.retro_reset =
- p_rarch->retro_reset_callback_original;
- p_rarch->retro_reset_callback_original = NULL;
- }
- if (p_rarch->retro_unserialize_callback_original)
- {
- p_rarch->current_core.retro_unserialize =
- p_rarch->retro_unserialize_callback_original;
- p_rarch->retro_unserialize_callback_original = NULL;
- }
- }
- static void *runahead_save_state_alloc(void)
- {
- struct rarch_state *p_rarch = &rarch_st;
- retro_ctx_serialize_info_t *savestate = (retro_ctx_serialize_info_t*)
- malloc(sizeof(retro_ctx_serialize_info_t));
- if (!savestate)
- return NULL;
- savestate->data = NULL;
- savestate->data_const = NULL;
- savestate->size = 0;
- if ( (p_rarch->runahead_save_state_size > 0) &&
- p_rarch->runahead_save_state_size_known)
- {
- savestate->data = malloc(p_rarch->runahead_save_state_size);
- savestate->data_const = savestate->data;
- savestate->size = p_rarch->runahead_save_state_size;
- }
- return savestate;
- }
- static void runahead_save_state_free(void *data)
- {
- retro_ctx_serialize_info_t *savestate = (retro_ctx_serialize_info_t*)data;
- if (!savestate)
- return;
- free(savestate->data);
- free(savestate);
- }
- static void runahead_save_state_list_init(
- struct rarch_state *p_rarch,
- size_t save_state_size)
- {
- p_rarch->runahead_save_state_size = save_state_size;
- p_rarch->runahead_save_state_size_known = true;
- mylist_create(&p_rarch->runahead_save_state_list, 16,
- runahead_save_state_alloc, runahead_save_state_free);
- }
- /* Hooks - Hooks to cleanup, and add dirty input hooks */
- static void runahead_remove_hooks(struct rarch_state *p_rarch)
- {
- if (p_rarch->original_retro_deinit)
- {
- p_rarch->current_core.retro_deinit = p_rarch->original_retro_deinit;
- p_rarch->original_retro_deinit = NULL;
- }
- if (p_rarch->original_retro_unload)
- {
- p_rarch->current_core.retro_unload_game = p_rarch->original_retro_unload;
- p_rarch->original_retro_unload = NULL;
- }
- remove_input_state_hook(p_rarch);
- }
- static void runahead_destroy(struct rarch_state *p_rarch)
- {
- mylist_destroy(&p_rarch->runahead_save_state_list);
- runahead_remove_hooks(p_rarch);
- runahead_clear_variables(p_rarch);
- }
- static void unload_hook(void)
- {
- struct rarch_state *p_rarch = &rarch_st;
- runahead_remove_hooks(p_rarch);
- runahead_destroy(p_rarch);
- secondary_core_destroy(p_rarch);
- if (p_rarch->current_core.retro_unload_game)
- p_rarch->current_core.retro_unload_game();
- p_rarch->core_poll_type_override = POLL_TYPE_OVERRIDE_DONTCARE;
- }
- static void runahead_deinit_hook(void)
- {
- struct rarch_state *p_rarch = &rarch_st;
- runahead_remove_hooks(p_rarch);
- runahead_destroy(p_rarch);
- secondary_core_destroy(p_rarch);
- if (p_rarch->current_core.retro_deinit)
- p_rarch->current_core.retro_deinit();
- }
- static void runahead_add_hooks(struct rarch_state *p_rarch)
- {
- if (!p_rarch->original_retro_deinit)
- {
- p_rarch->original_retro_deinit = p_rarch->current_core.retro_deinit;
- p_rarch->current_core.retro_deinit = runahead_deinit_hook;
- }
- if (!p_rarch->original_retro_unload)
- {
- p_rarch->original_retro_unload = p_rarch->current_core.retro_unload_game;
- p_rarch->current_core.retro_unload_game = unload_hook;
- }
- add_input_state_hook(p_rarch);
- }
- /* Runahead Code */
- static void runahead_error(struct rarch_state *p_rarch)
- {
- p_rarch->runahead_available = false;
- mylist_destroy(&p_rarch->runahead_save_state_list);
- runahead_remove_hooks(p_rarch);
- p_rarch->runahead_save_state_size = 0;
- p_rarch->runahead_save_state_size_known = true;
- }
- static bool runahead_create(struct rarch_state *p_rarch)
- {
- /* get savestate size and allocate buffer */
- retro_ctx_size_info_t info;
- p_rarch->request_fast_savestate = true;
- core_serialize_size(&info);
- p_rarch->request_fast_savestate = false;
- runahead_save_state_list_init(p_rarch, info.size);
- p_rarch->runahead_video_driver_is_active =
- p_rarch->video_driver_active;
- if ( (p_rarch->runahead_save_state_size == 0) ||
- !p_rarch->runahead_save_state_size_known)
- {
- runahead_error(p_rarch);
- return false;
- }
- runahead_add_hooks(p_rarch);
- p_rarch->runahead_force_input_dirty = true;
- if (p_rarch->runahead_save_state_list)
- mylist_resize(p_rarch->runahead_save_state_list, 1, true);
- return true;
- }
- static bool runahead_save_state(struct rarch_state *p_rarch)
- {
- retro_ctx_serialize_info_t *serialize_info;
- bool okay = false;
- if (!p_rarch->runahead_save_state_list)
- return false;
- serialize_info =
- (retro_ctx_serialize_info_t*)p_rarch->runahead_save_state_list->data[0];
- p_rarch->request_fast_savestate = true;
- okay = core_serialize(serialize_info);
- p_rarch->request_fast_savestate = false;
- if (okay)
- return true;
- runahead_error(p_rarch);
- return false;
- }
- static bool runahead_load_state(struct rarch_state *p_rarch)
- {
- bool okay = false;
- retro_ctx_serialize_info_t *serialize_info = (retro_ctx_serialize_info_t*)
- p_rarch->runahead_save_state_list->data[0];
- bool last_dirty = p_rarch->input_is_dirty;
- p_rarch->request_fast_savestate = true;
- /* calling core_unserialize has side effects with
- * netplay (it triggers transmitting your save state)
- call retro_unserialize directly from the core instead */
- okay = p_rarch->current_core.retro_unserialize(
- serialize_info->data_const, serialize_info->size);
- p_rarch->request_fast_savestate = false;
- p_rarch->input_is_dirty = last_dirty;
- if (!okay)
- runahead_error(p_rarch);
- return okay;
- }
- #if HAVE_DYNAMIC
- static bool runahead_load_state_secondary(struct rarch_state *p_rarch)
- {
- bool okay = false;
- retro_ctx_serialize_info_t *serialize_info =
- (retro_ctx_serialize_info_t*)p_rarch->runahead_save_state_list->data[0];
- p_rarch->request_fast_savestate = true;
- okay = secondary_core_deserialize(
- p_rarch,
- serialize_info->data_const, (int)serialize_info->size);
- p_rarch->request_fast_savestate = false;
- if (!okay)
- {
- p_rarch->runahead_secondary_core_available = false;
- runahead_error(p_rarch);
- return false;
- }
- return true;
- }
- #endif
- static bool runahead_core_run_use_last_input(struct rarch_state *p_rarch)
- {
- struct retro_callbacks *cbs = &p_rarch->retro_ctx;
- retro_input_poll_t old_poll_function = cbs->poll_cb;
- retro_input_state_t old_input_function = cbs->state_cb;
- cbs->poll_cb = retro_input_poll_null;
- cbs->state_cb = input_state_get_last;
- p_rarch->current_core.retro_set_input_poll(cbs->poll_cb);
- p_rarch->current_core.retro_set_input_state(cbs->state_cb);
- p_rarch->current_core.retro_run();
- cbs->poll_cb = old_poll_function;
- cbs->state_cb = old_input_function;
- p_rarch->current_core.retro_set_input_poll(cbs->poll_cb);
- p_rarch->current_core.retro_set_input_state(cbs->state_cb);
- return true;
- }
- static void do_runahead(
- struct rarch_state *p_rarch,
- int runahead_count, bool use_secondary)
- {
- int frame_number = 0;
- bool last_frame = false;
- bool suspended_frame = false;
- #if defined(HAVE_DYNAMIC) || defined(HAVE_DYLIB)
- const bool have_dynamic = true;
- #else
- const bool have_dynamic = false;
- #endif
- uint64_t frame_count = p_rarch->video_driver_frame_count;
- if (runahead_count <= 0 || !p_rarch->runahead_available)
- goto force_input_dirty;
- if (!p_rarch->runahead_save_state_size_known)
- {
- if (!runahead_create(p_rarch))
- {
- settings_t *settings = p_rarch->configuration_settings;
- bool runahead_hide_warnings = settings->bools.run_ahead_hide_warnings;
- if (!runahead_hide_warnings)
- runloop_msg_queue_push(msg_hash_to_str(MSG_RUNAHEAD_CORE_DOES_NOT_SUPPORT_SAVESTATES), 0, 2 * 60, true, NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO);
- goto force_input_dirty;
- }
- }
- /* Check for GUI */
- /* Hack: If we were in the GUI, force a resync. */
- if (frame_count != p_rarch->runahead_last_frame_count + 1)
- p_rarch->runahead_force_input_dirty = true;
- p_rarch->runahead_last_frame_count = frame_count;
- if ( !use_secondary
- || !have_dynamic
- || !p_rarch->runahead_secondary_core_available)
- {
- /* TODO: multiple savestates for higher performance
- * when not using secondary core */
- for (frame_number = 0; frame_number <= runahead_count; frame_number++)
- {
- last_frame = frame_number == runahead_count;
- suspended_frame = !last_frame;
- if (suspended_frame)
- {
- p_rarch->audio_suspended = true;
- p_rarch->video_driver_active = false;
- }
- if (frame_number == 0)
- core_run();
- else
- runahead_core_run_use_last_input(p_rarch);
- if (suspended_frame)
- {
- RUNAHEAD_RESUME_VIDEO(p_rarch);
- p_rarch->audio_suspended = false;
- }
- if (frame_number == 0)
- {
- if (!runahead_save_state(p_rarch))
- {
- runloop_msg_queue_push(msg_hash_to_str(MSG_RUNAHEAD_FAILED_TO_SAVE_STATE), 0, 3 * 60, true, NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO);
- return;
- }
- }
- if (last_frame)
- {
- if (!runahead_load_state(p_rarch))
- {
- runloop_msg_queue_push(msg_hash_to_str(MSG_RUNAHEAD_FAILED_TO_LOAD_STATE), 0, 3 * 60, true, NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO);
- return;
- }
- }
- }
- }
- else
- {
- #if HAVE_DYNAMIC
- if (!secondary_core_ensure_exists(p_rarch))
- {
- secondary_core_destroy(p_rarch);
- p_rarch->runahead_secondary_core_available = false;
- runloop_msg_queue_push(msg_hash_to_str(MSG_RUNAHEAD_FAILED_TO_CREATE_SECONDARY_INSTANCE), 0, 3 * 60, true, NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO);
- goto force_input_dirty;
- }
- /* run main core with video suspended */
- p_rarch->video_driver_active = false;
- core_run();
- RUNAHEAD_RESUME_VIDEO(p_rarch);
- if ( p_rarch->input_is_dirty
- || p_rarch->runahead_force_input_dirty)
- {
- p_rarch->input_is_dirty = false;
- if (!runahead_save_state(p_rarch))
- {
- runloop_msg_queue_push(msg_hash_to_str(MSG_RUNAHEAD_FAILED_TO_SAVE_STATE), 0, 3 * 60, true, NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO);
- return;
- }
- if (!runahead_load_state_secondary(p_rarch))
- {
- runloop_msg_queue_push(msg_hash_to_str(MSG_RUNAHEAD_FAILED_TO_LOAD_STATE), 0, 3 * 60, true, NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO);
- return;
- }
- for (frame_number = 0; frame_number < runahead_count - 1; frame_number++)
- {
- p_rarch->video_driver_active = false;
- p_rarch->audio_suspended = true;
- p_rarch->hard_disable_audio = true;
- RUNAHEAD_RUN_SECONDARY(p_rarch);
- p_rarch->hard_disable_audio = false;
- p_rarch->audio_suspended = false;
- RUNAHEAD_RESUME_VIDEO(p_rarch);
- }
- }
- p_rarch->audio_suspended = true;
- p_rarch->hard_disable_audio = true;
- RUNAHEAD_RUN_SECONDARY(p_rarch);
- p_rarch->hard_disable_audio = false;
- p_rarch->audio_suspended = false;
- #endif
- }
- p_rarch->runahead_force_input_dirty = false;
- return;
- force_input_dirty:
- core_run();
- p_rarch->runahead_force_input_dirty = true;
- }
- #endif
- static retro_time_t rarch_core_runtime_tick(
- struct rarch_state *p_rarch,
- retro_time_t current_time)
- {
- retro_time_t frame_time =
- (1.0 / p_rarch->video_driver_av_info.timing.fps) * 1000000;
- bool runloop_slowmotion = p_rarch->runloop_slowmotion;
- bool runloop_fastmotion = p_rarch->runloop_fastmotion;
- /* Account for slow motion */
- if (runloop_slowmotion)
- {
- settings_t *settings = p_rarch->configuration_settings;
- float slowmotion_ratio = settings->floats.slowmotion_ratio;
- return (retro_time_t)((double)frame_time * slowmotion_ratio);
- }
- /* Account for fast forward */
- if (runloop_fastmotion)
- {
- /* Doing it this way means we miss the first frame after
- * turning fast forward on, but it saves the overhead of
- * having to do:
- * retro_time_t current_usec = cpu_features_get_time_usec();
- * libretro_core_runtime_last = current_usec;
- * every frame when fast forward is off. */
- retro_time_t current_usec = current_time;
- retro_time_t potential_frame_time = current_usec -
- p_rarch->libretro_core_runtime_last;
- p_rarch->libretro_core_runtime_last = current_usec;
- if (potential_frame_time < frame_time)
- return potential_frame_time;
- }
- return frame_time;
- }
- static void retroarch_print_features(void)
- {
- char buf[2048];
- buf[0] = '\0';
- frontend_driver_attach_console();
- strlcat(buf, "\nFeatures:\n", sizeof(buf));
- _PSUPP_BUF(buf, SUPPORTS_LIBRETRODB, "LibretroDB", "LibretroDB support");
- _PSUPP_BUF(buf, SUPPORTS_COMMAND, "Command", "Command interface support");
- _PSUPP_BUF(buf, SUPPORTS_NETWORK_COMMAND, "Network Command", "Network Command interface "
- "support");
- _PSUPP_BUF(buf, SUPPORTS_SDL, "SDL", "SDL input/audio/video drivers");
- _PSUPP_BUF(buf, SUPPORTS_SDL2, "SDL2", "SDL2 input/audio/video drivers");
- _PSUPP_BUF(buf, SUPPORTS_X11, "X11", "X11 input/video drivers");
- _PSUPP_BUF(buf, SUPPORTS_WAYLAND, "wayland", "Wayland input/video drivers");
- _PSUPP_BUF(buf, SUPPORTS_THREAD, "Threads", "Threading support");
- _PSUPP_BUF(buf, SUPPORTS_VULKAN, "Vulkan", "Vulkan video driver");
- _PSUPP_BUF(buf, SUPPORTS_METAL, "Metal", "Metal video driver");
- _PSUPP_BUF(buf, SUPPORTS_OPENGL, "OpenGL", "OpenGL video driver support");
- _PSUPP_BUF(buf, SUPPORTS_OPENGLES, "OpenGL ES", "OpenGLES video driver support");
- _PSUPP_BUF(buf, SUPPORTS_XVIDEO, "XVideo", "Video driver");
- _PSUPP_BUF(buf, SUPPORTS_UDEV, "UDEV", "UDEV/EVDEV input driver support");
- _PSUPP_BUF(buf, SUPPORTS_EGL, "EGL", "Video context driver");
- _PSUPP_BUF(buf, SUPPORTS_KMS, "KMS", "Video context driver");
- _PSUPP_BUF(buf, SUPPORTS_VG, "OpenVG", "Video context driver");
- _PSUPP_BUF(buf, SUPPORTS_COREAUDIO, "CoreAudio", "Audio driver");
- _PSUPP_BUF(buf, SUPPORTS_COREAUDIO3, "CoreAudioV3", "Audio driver");
- _PSUPP_BUF(buf, SUPPORTS_ALSA, "ALSA", "Audio driver");
- _PSUPP_BUF(buf, SUPPORTS_OSS, "OSS", "Audio driver");
- _PSUPP_BUF(buf, SUPPORTS_JACK, "Jack", "Audio driver");
- _PSUPP_BUF(buf, SUPPORTS_RSOUND, "RSound", "Audio driver");
- _PSUPP_BUF(buf, SUPPORTS_ROAR, "RoarAudio", "Audio driver");
- _PSUPP_BUF(buf, SUPPORTS_PULSE, "PulseAudio", "Audio driver");
- _PSUPP_BUF(buf, SUPPORTS_DSOUND, "DirectSound", "Audio driver");
- _PSUPP_BUF(buf, SUPPORTS_WASAPI, "WASAPI", "Audio driver");
- _PSUPP_BUF(buf, SUPPORTS_XAUDIO, "XAudio2", "Audio driver");
- _PSUPP_BUF(buf, SUPPORTS_AL, "OpenAL", "Audio driver");
- _PSUPP_BUF(buf, SUPPORTS_SL, "OpenSL", "Audio driver");
- _PSUPP_BUF(buf, SUPPORTS_7ZIP, "7zip", "7zip extraction support");
- _PSUPP_BUF(buf, SUPPORTS_ZLIB, "zlib", ".zip extraction support");
- _PSUPP_BUF(buf, SUPPORTS_DYLIB, "External", "External filter and plugin support");
- _PSUPP_BUF(buf, SUPPORTS_CG, "Cg", "Fragment/vertex shader driver");
- _PSUPP_BUF(buf, SUPPORTS_GLSL, "GLSL", "Fragment/vertex shader driver");
- _PSUPP_BUF(buf, SUPPORTS_HLSL, "HLSL", "Fragment/vertex shader driver");
- _PSUPP_BUF(buf, SUPPORTS_SDL_IMAGE, "SDL_image", "SDL_image image loading");
- _PSUPP_BUF(buf, SUPPORTS_RPNG, "rpng", "PNG image loading/encoding");
- _PSUPP_BUF(buf, SUPPORTS_RJPEG, "rjpeg", "JPEG image loading");
- _PSUPP_BUF(buf, SUPPORTS_DYNAMIC, "Dynamic", "Dynamic run-time loading of "
- "libretro library");
- _PSUPP_BUF(buf, SUPPORTS_FFMPEG, "FFmpeg", "On-the-fly recording of gameplay "
- "with libavcodec");
- _PSUPP_BUF(buf, SUPPORTS_FREETYPE, "FreeType", "TTF font rendering driver");
- _PSUPP_BUF(buf, SUPPORTS_CORETEXT, "CoreText", "TTF font rendering driver ");
- _PSUPP_BUF(buf, SUPPORTS_NETPLAY, "Netplay", "Peer-to-peer netplay");
- _PSUPP_BUF(buf, SUPPORTS_PYTHON, "Python", "Script support in shaders");
- _PSUPP_BUF(buf, SUPPORTS_LIBUSB, "Libusb", "Libusb support");
- _PSUPP_BUF(buf, SUPPORTS_COCOA, "Cocoa", "Cocoa UI companion support "
- "(for OSX and/or iOS)");
- _PSUPP_BUF(buf, SUPPORTS_QT, "Qt", "Qt UI companion support");
- _PSUPP_BUF(buf, SUPPORTS_V4L2, "Video4Linux2", "Camera driver");
- puts(buf);
- }
- static void retroarch_print_version(void)
- {
- char str[255];
- frontend_driver_attach_console();
- str[0] = '\0';
- fprintf(stderr, "%s: %s -- v%s",
- msg_hash_to_str(MSG_PROGRAM),
- msg_hash_to_str(MSG_LIBRETRO_FRONTEND),
- PACKAGE_VERSION);
- #ifdef HAVE_GIT_VERSION
- printf(" -- %s --\n", retroarch_git_version);
- #else
- printf("\n");
- #endif
- retroarch_get_capabilities(RARCH_CAPABILITIES_COMPILER, str, sizeof(str));
- strlcat(str, " Built: " __DATE__, sizeof(str));
- fprintf(stdout, "%s\n", str);
- }
- /**
- * retroarch_print_help:
- *
- * Prints help message explaining the program's commandline switches.
- **/
- static void retroarch_print_help(const char *arg0)
- {
- frontend_driver_attach_console();
- puts("===================================================================");
- retroarch_print_version();
- puts("===================================================================");
- printf("Usage: %s [OPTIONS]... [FILE]\n", arg0);
- {
- char buf[2148];
- buf[0] = '\0';
- strlcpy(buf, " -h, --help Show this help message.\n", sizeof(buf));
- strlcat(buf, " -v, --verbose Verbose logging.\n", sizeof(buf));
- strlcat(buf, " --log-file FILE Log messages to FILE.\n", sizeof(buf));
- strlcat(buf, " --version Show version.\n", sizeof(buf));
- strlcat(buf, " --features Prints available features compiled into "
- "program.\n", sizeof(buf));
- #ifdef HAVE_MENU
- strlcat(buf, " --menu Do not require content or libretro core to "
- "be loaded,\n"
- " starts directly in menu. If no arguments "
- "are passed to\n"
- " the program, it is equivalent to using "
- "--menu as only argument.\n", sizeof(buf));
- #endif
- strlcat(buf, " -s, --save=PATH Path for save files (*.srm). (DEPRECATED, use --appendconfig and savefile_directory)\n", sizeof(buf));
- strlcat(buf, " -S, --savestate=PATH Path for the save state files (*.state). (DEPRECATED, use --appendconfig and savestate_directory)\n", sizeof(buf));
- strlcat(buf, " --set-shader PATH Path to a shader (preset) that will be loaded each time content is loaded.\n"
- " Effectively overrides automatic shader presets.\n"
- " An empty argument \"\" will disable automatic shader presets.\n", sizeof(buf));
- strlcat(buf, " -f, --fullscreen Start the program in fullscreen regardless "
- "of config settings.\n", sizeof(buf));
- #ifdef HAVE_CONFIGFILE
- #ifdef _WIN32
- strlcat(buf, " -c, --config=FILE Path for config file."
- "\n\t\tDefaults to retroarch.cfg in same directory as retroarch.exe."
- "\n\t\tIf a default config is not found, the program will attempt to "
- "create one.\n"
- , sizeof(buf));
- #else
- strlcat(buf, " -c, --config=FILE Path for config file."
- "\n\t\tBy default looks for config in $XDG_CONFIG_HOME/retroarch/"
- "retroarch.cfg,\n\t\t$HOME/.config/retroarch/retroarch.cfg,\n\t\t"
- "and $HOME/.retroarch.cfg.\n\t\tIf a default config is not found, "
- "the program will attempt to create one based on the \n\t\t"
- "skeleton config (" GLOBAL_CONFIG_DIR "/retroarch.cfg). \n"
- , sizeof(buf));
- #endif
- #endif
- strlcat(buf, " --appendconfig=FILE\n"
- " Extra config files are loaded in, "
- "and take priority over\n"
- " config selected in -c (or default). "
- "Multiple configs are\n"
- " delimited by '|'.\n", sizeof(buf));
- #ifdef HAVE_DYNAMIC
- strlcat(buf, " -L, --libretro=FILE Path to libretro implementation. "
- "Overrides any config setting.\n", sizeof(buf));
- #endif
- strlcat(buf, " --subsystem=NAME Use a subsystem of the libretro core. "
- "Multiple content\n"
- " files are loaded as multiple arguments. "
- "If a content\n"
- " file is skipped, use a blank (\"\") "
- "command line argument.\n"
- " Content must be loaded in an order "
- "which depends on the\n"
- " particular subsystem used. See verbose "
- "log output to learn\n"
- " how a particular subsystem wants content "
- "to be loaded.\n", sizeof(buf));
- puts(buf);
- }
- printf(" -N, --nodevice=PORT\n"
- " Disconnects controller device connected "
- "to PORT (1 to %d).\n", MAX_USERS);
- printf(" -A, --dualanalog=PORT\n"
- " Connect a DualAnalog controller to PORT "
- "(1 to %d).\n", MAX_USERS);
- printf(" -d, --device=PORT:ID\n"
- " Connect a generic device into PORT of "
- "the device (1 to %d).\n", MAX_USERS);
- {
- char buf[2560];
- buf[0] = '\0';
- strlcpy(buf, " Format is PORT:ID, where ID is a number "
- "corresponding to the particular device.\n", sizeof(buf));
- #ifdef HAVE_BSV_MOVIE
- strlcat(buf, " -P, --bsvplay=FILE Playback a BSV movie file.\n", sizeof(buf));
- strlcat(buf, " -R, --bsvrecord=FILE Start recording a BSV movie file from "
- "the beginning.\n", sizeof(buf));
- strlcat(buf, " --eof-exit Exit upon reaching the end of the "
- "BSV movie file.\n", sizeof(buf));
- #endif
- strlcat(buf, " -M, --sram-mode=MODE SRAM handling mode. MODE can be "
- "'noload-nosave',\n"
- " 'noload-save', 'load-nosave' or "
- "'load-save'.\n"
- " Note: 'noload-save' implies that "
- "save files *WILL BE OVERWRITTEN*.\n", sizeof(buf));
- #ifdef HAVE_NETWORKING
- strlcat(buf, " -H, --host Host netplay as user 1.\n", sizeof(buf));
- strlcat(buf, " -C, --connect=HOST Connect to netplay server as user 2.\n", sizeof(buf));
- strlcat(buf, " --port=PORT Port used to netplay. Default is 55435.\n", sizeof(buf));
- strlcat(buf, " --stateless Use \"stateless\" mode for netplay\n", sizeof(buf));
- strlcat(buf, " (requires a very fast network).\n", sizeof(buf));
- strlcat(buf, " --check-frames=NUMBER\n"
- " Check frames when using netplay.\n", sizeof(buf));
- #ifdef HAVE_NETWORK_CMD
- strlcat(buf, " --command Sends a command over UDP to an already "
- "running program process.\n", sizeof(buf));
- strlcat(buf, " Available commands are listed if command is invalid.\n", sizeof(buf));
- #endif
- #endif
- strlcat(buf, " --nick=NICK Picks a username (for use with netplay). "
- "Not mandatory.\n", sizeof(buf));
- strlcat(buf, " -r, --record=FILE Path to record video file.\n "
- "Using .mkv extension is recommended.\n", sizeof(buf));
- strlcat(buf, " --recordconfig Path to settings used during recording.\n", sizeof(buf));
- strlcat(buf, " --size=WIDTHxHEIGHT\n"
- " Overrides output video size when recording.\n", sizeof(buf));
- #ifdef HAVE_PATCH
- strlcat(buf, " -U, --ups=FILE Specifies path for UPS patch that will be "
- "applied to content.\n", sizeof(buf));
- strlcat(buf, " --bps=FILE Specifies path for BPS patch that will be "
- "applied to content.\n", sizeof(buf));
- strlcat(buf, " --ips=FILE Specifies path for IPS patch that will be "
- "applied to content.\n", sizeof(buf));
- strlcat(buf, " --no-patch Disables all forms of content patching.\n", sizeof(buf));
- #endif
- strlcat(buf, " -D, --detach Detach program from the running console. "
- "Not relevant for all platforms.\n", sizeof(buf));
- strlcat(buf, " --max-frames=NUMBER\n"
- " Runs for the specified number of frames, "
- "then exits.\n", sizeof(buf));
- #ifdef HAVE_SCREENSHOTS
- strlcat(buf, " --max-frames-ss\n"
- " Takes a screenshot at the end of max-frames.\n", sizeof(buf));
- strlcat(buf, " --max-frames-ss-path=FILE\n"
- " Path to save the screenshot to at the end of max-frames.\n", sizeof(buf));
- #endif
- #ifdef HAVE_ACCESSIBILITY
- strlcat(buf, " --accessibility\n"
- " Enables accessibilty for blind users using text-to-speech.\n", sizeof(buf));
- #endif
- strlcat(buf, " --load-menu-on-error\n"
- " Open menu instead of quitting if specified core or content fails to load.\n", sizeof(buf));
- puts(buf);
- }
- }
- /**
- * retroarch_parse_input_and_config:
- * @argc : Count of (commandline) arguments.
- * @argv : (Commandline) arguments.
- *
- * Parses (commandline) arguments passed to program and loads the config file,
- * with command line options overriding the config file.
- *
- **/
- static void retroarch_parse_input_and_config(
- struct rarch_state *p_rarch,
- global_t *global,
- int argc, char *argv[])
- {
- unsigned i;
- static bool first_run = true;
- const char *optstring = NULL;
- bool explicit_menu = false;
- bool cli_active = false;
- bool cli_core_set = false;
- bool cli_content_set = false;
- const struct option opts[] = {
- #ifdef HAVE_DYNAMIC
- { "libretro", 1, NULL, 'L' },
- #endif
- { "menu", 0, NULL, RA_OPT_MENU },
- { "help", 0, NULL, 'h' },
- { "save", 1, NULL, 's' },
- { "fullscreen", 0, NULL, 'f' },
- { "record", 1, NULL, 'r' },
- { "recordconfig", 1, NULL, RA_OPT_RECORDCONFIG },
- { "size", 1, NULL, RA_OPT_SIZE },
- { "verbose", 0, NULL, 'v' },
- #ifdef HAVE_CONFIGFILE
- { "config", 1, NULL, 'c' },
- { "appendconfig", 1, NULL, RA_OPT_APPENDCONFIG },
- #endif
- { "nodevice", 1, NULL, 'N' },
- { "dualanalog", 1, NULL, 'A' },
- { "device", 1, NULL, 'd' },
- { "savestate", 1, NULL, 'S' },
- { "set-shader", 1, NULL, RA_OPT_SET_SHADER },
- #ifdef HAVE_BSV_MOVIE
- { "bsvplay", 1, NULL, 'P' },
- { "bsvrecord", 1, NULL, 'R' },
- #endif
- { "sram-mode", 1, NULL, 'M' },
- #ifdef HAVE_NETWORKING
- { "host", 0, NULL, 'H' },
- { "connect", 1, NULL, 'C' },
- { "stateless", 0, NULL, RA_OPT_STATELESS },
- { "check-frames", 1, NULL, RA_OPT_CHECK_FRAMES },
- { "port", 1, NULL, RA_OPT_PORT },
- #ifdef HAVE_NETWORK_CMD
- { "command", 1, NULL, RA_OPT_COMMAND },
- #endif
- #endif
- { "nick", 1, NULL, RA_OPT_NICK },
- #ifdef HAVE_PATCH
- { "ups", 1, NULL, 'U' },
- { "bps", 1, NULL, RA_OPT_BPS },
- { "ips", 1, NULL, RA_OPT_IPS },
- { "no-patch", 0, NULL, RA_OPT_NO_PATCH },
- #endif
- { "detach", 0, NULL, 'D' },
- { "features", 0, NULL, RA_OPT_FEATURES },
- { "subsystem", 1, NULL, RA_OPT_SUBSYSTEM },
- { "max-frames", 1, NULL, RA_OPT_MAX_FRAMES },
- { "max-frames-ss", 0, NULL, RA_OPT_MAX_FRAMES_SCREENSHOT },
- { "max-frames-ss-path", 1, NULL, RA_OPT_MAX_FRAMES_SCREENSHOT_PATH },
- { "eof-exit", 0, NULL, RA_OPT_EOF_EXIT },
- { "version", 0, NULL, RA_OPT_VERSION },
- { "log-file", 1, NULL, RA_OPT_LOG_FILE },
- { "accessibility", 0, NULL, RA_OPT_ACCESSIBILITY},
- { "load-menu-on-error", 0, NULL, RA_OPT_LOAD_MENU_ON_ERROR },
- { NULL, 0, NULL, 0 }
- };
- if (first_run)
- {
- /* Copy the args into a buffer so launch arguments can be reused */
- for (i = 0; i < (unsigned)argc; i++)
- {
- strlcat(p_rarch->launch_arguments,
- argv[i], sizeof(p_rarch->launch_arguments));
- strlcat(p_rarch->launch_arguments, " ",
- sizeof(p_rarch->launch_arguments));
- }
- string_trim_whitespace_left(p_rarch->launch_arguments);
- string_trim_whitespace_right(p_rarch->launch_arguments);
- first_run = false;
- /* Command line interface is only considered
- * to be 'active' (i.e. used by a third party)
- * if this is the first run (subsequent runs
- * are triggered by RetroArch itself) */
- cli_active = true;
- }
- /* Handling the core type is finicky. Based on the arguments we pass in,
- * we handle it differently.
- * Some current cases which track desired behavior and how it is supposed to work:
- *
- * Dynamically linked RA:
- * ./retroarch -> CORE_TYPE_DUMMY
- * ./retroarch -v -> CORE_TYPE_DUMMY + verbose
- * ./retroarch --menu -> CORE_TYPE_DUMMY
- * ./retroarch --menu -v -> CORE_TYPE_DUMMY + verbose
- * ./retroarch -L contentless-core -> CORE_TYPE_PLAIN
- * ./retroarch -L content-core -> CORE_TYPE_PLAIN + FAIL (This currently crashes)
- * ./retroarch [-L content-core] ROM -> CORE_TYPE_PLAIN
- * ./retroarch <-L or ROM> --menu -> FAIL
- *
- * The heuristic here seems to be that if we use the -L CLI option or
- * optind < argc at the end we should set CORE_TYPE_PLAIN.
- * To handle --menu, we should ensure that CORE_TYPE_DUMMY is still set
- * otherwise, fail early, since the CLI options are non-sensical.
- * We could also simply ignore --menu in this case to be more friendly with
- * bogus arguments.
- */
- if (!p_rarch->has_set_core)
- retroarch_set_current_core_type(CORE_TYPE_DUMMY, false);
- path_clear(RARCH_PATH_SUBSYSTEM);
- retroarch_override_setting_free_state();
- p_rarch->has_set_username = false;
- #ifdef HAVE_PATCH
- rarch_ctl(RARCH_CTL_UNSET_UPS_PREF, NULL);
- rarch_ctl(RARCH_CTL_UNSET_IPS_PREF, NULL);
- rarch_ctl(RARCH_CTL_UNSET_BPS_PREF, NULL);
- *global->name.ups = '\0';
- *global->name.bps = '\0';
- *global->name.ips = '\0';
- #endif
- #ifdef HAVE_CONFIGFILE
- p_rarch->runloop_overrides_active = false;
- #endif
- global->cli_load_menu_on_error = false;
- /* Make sure we can call retroarch_parse_input several times ... */
- optind = 0;
- optstring = "hs:fvS:A:U:DN:d:"
- BSV_MOVIE_ARG NETPLAY_ARG DYNAMIC_ARG FFMPEG_RECORD_ARG CONFIG_FILE_ARG;
- #ifdef ORBIS
- argv = &(argv[2]);
- argc = argc - 2;
- #endif
- #ifndef HAVE_MENU
- if (argc == 1)
- {
- printf("%s\n", msg_hash_to_str(MSG_NO_ARGUMENTS_SUPPLIED_AND_NO_MENU_BUILTIN));
- retroarch_print_help(argv[0]);
- exit(0);
- }
- #endif
- /* First pass: Read the config file path and any directory overrides, so
- * they're in place when we load the config */
- if (argc)
- {
- for (;;)
- {
- int c = getopt_long(argc, argv, optstring, opts, NULL);
- #if 0
- fprintf(stderr, "c is: %c (%d), optarg is: [%s]\n", c, c, string_is_empty(optarg) ? "" : optarg);
- #endif
- if (c == -1)
- break;
- switch (c)
- {
- case 'h':
- retroarch_print_help(argv[0]);
- exit(0);
- #ifdef HAVE_CONFIGFILE
- case 'c':
- path_set(RARCH_PATH_CONFIG, optarg);
- break;
- case RA_OPT_APPENDCONFIG:
- path_set(RARCH_PATH_CONFIG_APPEND, optarg);
- break;
- #endif
- case 's':
- strlcpy(global->name.savefile, optarg,
- sizeof(global->name.savefile));
- retroarch_override_setting_set(
- RARCH_OVERRIDE_SETTING_SAVE_PATH, NULL);
- break;
- case 'S':
- strlcpy(global->name.savestate, optarg,
- sizeof(global->name.savestate));
- retroarch_override_setting_set(
- RARCH_OVERRIDE_SETTING_STATE_PATH, NULL);
- break;
- /* Must handle '?' otherwise you get an infinite loop */
- case '?':
- retroarch_print_help(argv[0]);
- retroarch_fail(1, "retroarch_parse_input()");
- break;
- /* All other arguments are handled in the second pass */
- }
- }
- }
- /* Flush out some states that could have been set
- * by core environment variables. */
- p_rarch->current_core.has_set_input_descriptors = false;
- p_rarch->current_core.has_set_subsystems = false;
- /* Load the config file now that we know what it is */
- #ifdef HAVE_CONFIGFILE
- if (!p_rarch->rarch_block_config_read)
- #endif
- {
- /* If this is a static build, load salamander
- * config file first (sets RARCH_PATH_CORE) */
- #if !defined(HAVE_DYNAMIC)
- config_load_file_salamander();
- #endif
- config_load(&p_rarch->g_extern);
- }
- /* Second pass: All other arguments override the config file */
- optind = 1;
- if (argc)
- {
- for (;;)
- {
- int c = getopt_long(argc, argv, optstring, opts, NULL);
- if (c == -1)
- break;
- switch (c)
- {
- case 'd':
- {
- unsigned new_port;
- unsigned id = 0;
- struct string_list *list = string_split(optarg, ":");
- int port = 0;
- if (list && list->size == 2)
- {
- port = (int)strtol(list->elems[0].data, NULL, 0);
- id = (unsigned)strtoul(list->elems[1].data, NULL, 0);
- }
- string_list_free(list);
- if (port < 1 || port > MAX_USERS)
- {
- RARCH_ERR("%s\n", msg_hash_to_str(MSG_VALUE_CONNECT_DEVICE_FROM_A_VALID_PORT));
- retroarch_print_help(argv[0]);
- retroarch_fail(1, "retroarch_parse_input()");
- }
- new_port = port -1;
- input_config_set_device(new_port, id);
- retroarch_override_setting_set(
- RARCH_OVERRIDE_SETTING_LIBRETRO_DEVICE, &new_port);
- }
- break;
- case 'A':
- {
- unsigned new_port;
- int port = (int)strtol(optarg, NULL, 0);
- if (port < 1 || port > MAX_USERS)
- {
- RARCH_ERR("Connect dualanalog to a valid port.\n");
- retroarch_print_help(argv[0]);
- retroarch_fail(1, "retroarch_parse_input()");
- }
- new_port = port - 1;
- input_config_set_device(new_port, RETRO_DEVICE_ANALOG);
- retroarch_override_setting_set(
- RARCH_OVERRIDE_SETTING_LIBRETRO_DEVICE, &new_port);
- }
- break;
- case 'f':
- p_rarch->rarch_force_fullscreen = true;
- break;
- case 'v':
- verbosity_enable();
- retroarch_override_setting_set(
- RARCH_OVERRIDE_SETTING_VERBOSITY, NULL);
- break;
- case 'N':
- {
- unsigned new_port;
- int port = (int)strtol(optarg, NULL, 0);
- if (port < 1 || port > MAX_USERS)
- {
- RARCH_ERR("%s\n",
- msg_hash_to_str(MSG_DISCONNECT_DEVICE_FROM_A_VALID_PORT));
- retroarch_print_help(argv[0]);
- retroarch_fail(1, "retroarch_parse_input()");
- }
- new_port = port - 1;
- input_config_set_device(port - 1, RETRO_DEVICE_NONE);
- retroarch_override_setting_set(
- RARCH_OVERRIDE_SETTING_LIBRETRO_DEVICE, &new_port);
- }
- break;
- case 'r':
- strlcpy(global->record.path, optarg,
- sizeof(global->record.path));
- if (p_rarch->recording_enable)
- p_rarch->recording_enable = true;
- break;
- case RA_OPT_SET_SHADER:
- #if defined(HAVE_CG) || defined(HAVE_GLSL) || defined(HAVE_SLANG) || defined(HAVE_HLSL)
- /* disable auto-shaders */
- if (string_is_empty(optarg))
- {
- p_rarch->cli_shader_disable = true;
- break;
- }
- /* rebase on shader directory */
- if (!path_is_absolute(optarg))
- {
- settings_t *settings = p_rarch->configuration_settings;
- char *ref_path = settings->paths.directory_video_shader;
- fill_pathname_join(p_rarch->cli_shader,
- ref_path, optarg, sizeof(p_rarch->cli_shader));
- break;
- }
- strlcpy(p_rarch->cli_shader, optarg, sizeof(p_rarch->cli_shader));
- #endif
- break;
- #ifdef HAVE_DYNAMIC
- case 'L':
- {
- int path_stats;
- if (string_ends_with_size(optarg, "builtin",
- strlen(optarg), STRLEN_CONST("builtin")))
- {
- RARCH_LOG("--libretro argument \"%s\" is a built-in core. Ignoring.\n",
- optarg);
- break;
- }
- path_stats = path_stat(optarg);
- if ((path_stats & RETRO_VFS_STAT_IS_DIRECTORY) != 0)
- {
- settings_t *settings = p_rarch->configuration_settings;
- path_clear(RARCH_PATH_CORE);
- configuration_set_string(settings,
- settings->paths.directory_libretro, optarg);
- retroarch_override_setting_set(RARCH_OVERRIDE_SETTING_LIBRETRO, NULL);
- retroarch_override_setting_set(RARCH_OVERRIDE_SETTING_LIBRETRO_DIRECTORY, NULL);
- RARCH_WARN("Using old --libretro behavior. "
- "Setting libretro_directory to \"%s\" instead.\n",
- optarg);
- }
- else if ((path_stats & RETRO_VFS_STAT_IS_VALID) != 0)
- {
- path_set(RARCH_PATH_CORE, optarg);
- retroarch_override_setting_set(RARCH_OVERRIDE_SETTING_LIBRETRO, NULL);
- /* We requested explicit core, so use PLAIN core type. */
- retroarch_set_current_core_type(CORE_TYPE_PLAIN, false);
- }
- else
- {
- RARCH_WARN("--libretro argument \"%s\" is neither a file nor directory. Ignoring.\n",
- optarg);
- }
- }
- break;
- #endif
- case 'P':
- #ifdef HAVE_BSV_MOVIE
- strlcpy(p_rarch->bsv_movie_state.movie_start_path, optarg,
- sizeof(p_rarch->bsv_movie_state.movie_start_path));
- p_rarch->bsv_movie_state.movie_start_playback = true;
- p_rarch->bsv_movie_state.movie_start_recording = false;
- #endif
- break;
- case 'R':
- #ifdef HAVE_BSV_MOVIE
- strlcpy(p_rarch->bsv_movie_state.movie_start_path, optarg,
- sizeof(p_rarch->bsv_movie_state.movie_start_path));
- p_rarch->bsv_movie_state.movie_start_playback = false;
- p_rarch->bsv_movie_state.movie_start_recording = true;
- #endif
- break;
- case 'M':
- if (string_is_equal(optarg, "noload-nosave"))
- {
- p_rarch->rarch_is_sram_load_disabled = true;
- p_rarch->rarch_is_sram_save_disabled = true;
- }
- else if (string_is_equal(optarg, "noload-save"))
- p_rarch->rarch_is_sram_load_disabled = true;
- else if (string_is_equal(optarg, "load-nosave"))
- p_rarch->rarch_is_sram_save_disabled = true;
- else if (string_is_not_equal(optarg, "load-save"))
- {
- RARCH_ERR("Invalid argument in --sram-mode.\n");
- retroarch_print_help(argv[0]);
- retroarch_fail(1, "retroarch_parse_input()");
- }
- break;
- #ifdef HAVE_NETWORKING
- case 'H':
- retroarch_override_setting_set(
- RARCH_OVERRIDE_SETTING_NETPLAY_MODE, NULL);
- netplay_driver_ctl(RARCH_NETPLAY_CTL_ENABLE_SERVER, NULL);
- break;
- case 'C':
- {
- settings_t *settings = p_rarch->configuration_settings;
- retroarch_override_setting_set(
- RARCH_OVERRIDE_SETTING_NETPLAY_MODE, NULL);
- retroarch_override_setting_set(
- RARCH_OVERRIDE_SETTING_NETPLAY_IP_ADDRESS, NULL);
- netplay_driver_ctl(RARCH_NETPLAY_CTL_ENABLE_CLIENT, NULL);
- configuration_set_string(settings,
- settings->paths.netplay_server, optarg);
- }
- break;
- case RA_OPT_STATELESS:
- {
- settings_t *settings = p_rarch->configuration_settings;
- configuration_set_bool(settings,
- settings->bools.netplay_stateless_mode, true);
- retroarch_override_setting_set(
- RARCH_OVERRIDE_SETTING_NETPLAY_STATELESS_MODE, NULL);
- }
- break;
- case RA_OPT_CHECK_FRAMES:
- {
- settings_t *settings = p_rarch->configuration_settings;
- retroarch_override_setting_set(
- RARCH_OVERRIDE_SETTING_NETPLAY_CHECK_FRAMES, NULL);
- configuration_set_int(settings,
- settings->ints.netplay_check_frames,
- (int)strtoul(optarg, NULL, 0));
- }
- break;
- case RA_OPT_PORT:
- {
- settings_t *settings = p_rarch->configuration_settings;
- retroarch_override_setting_set(
- RARCH_OVERRIDE_SETTING_NETPLAY_IP_PORT, NULL);
- configuration_set_uint(settings,
- settings->uints.netplay_port,
- (int)strtoul(optarg, NULL, 0));
- }
- break;
- #ifdef HAVE_NETWORK_CMD
- case RA_OPT_COMMAND:
- #ifdef HAVE_COMMAND
- if (command_network_send((const char*)optarg))
- exit(0);
- else
- retroarch_fail(1, "network_cmd_send()");
- #endif
- break;
- #endif
- #endif
- case RA_OPT_BPS:
- #ifdef HAVE_PATCH
- strlcpy(global->name.bps, optarg,
- sizeof(global->name.bps));
- p_rarch->rarch_bps_pref = true;
- retroarch_override_setting_set(RARCH_OVERRIDE_SETTING_BPS_PREF, NULL);
- #endif
- break;
- case 'U':
- #ifdef HAVE_PATCH
- strlcpy(global->name.ups, optarg,
- sizeof(global->name.ups));
- p_rarch->rarch_ups_pref = true;
- retroarch_override_setting_set(RARCH_OVERRIDE_SETTING_UPS_PREF, NULL);
- #endif
- break;
- case RA_OPT_IPS:
- #ifdef HAVE_PATCH
- strlcpy(global->name.ips, optarg,
- sizeof(global->name.ips));
- p_rarch->rarch_ips_pref = true;
- retroarch_override_setting_set(RARCH_OVERRIDE_SETTING_IPS_PREF, NULL);
- #endif
- break;
- case RA_OPT_NO_PATCH:
- #ifdef HAVE_PATCH
- p_rarch->rarch_patch_blocked = true;
- #endif
- break;
- case 'D':
- frontend_driver_detach_console();
- break;
- case RA_OPT_MENU:
- explicit_menu = true;
- break;
- case RA_OPT_NICK:
- {
- settings_t *settings = p_rarch->configuration_settings;
- p_rarch->has_set_username = true;
- configuration_set_string(settings,
- settings->paths.username, optarg);
- }
- break;
- case RA_OPT_SIZE:
- if (sscanf(optarg, "%ux%u",
- &p_rarch->recording_width,
- &p_rarch->recording_height) != 2)
- {
- RARCH_ERR("Wrong format for --size.\n");
- retroarch_print_help(argv[0]);
- retroarch_fail(1, "retroarch_parse_input()");
- }
- break;
- case RA_OPT_RECORDCONFIG:
- strlcpy(global->record.config, optarg,
- sizeof(global->record.config));
- break;
- case RA_OPT_MAX_FRAMES:
- p_rarch->runloop_max_frames = (unsigned)strtoul(optarg, NULL, 10);
- break;
- case RA_OPT_MAX_FRAMES_SCREENSHOT:
- #ifdef HAVE_SCREENSHOTS
- p_rarch->runloop_max_frames_screenshot = true;
- #endif
- break;
- case RA_OPT_MAX_FRAMES_SCREENSHOT_PATH:
- #ifdef HAVE_SCREENSHOTS
- strlcpy(p_rarch->runloop_max_frames_screenshot_path,
- optarg,
- sizeof(p_rarch->runloop_max_frames_screenshot_path));
- #endif
- break;
- case RA_OPT_SUBSYSTEM:
- path_set(RARCH_PATH_SUBSYSTEM, optarg);
- break;
- case RA_OPT_FEATURES:
- retroarch_print_features();
- exit(0);
- case RA_OPT_EOF_EXIT:
- #ifdef HAVE_BSV_MOVIE
- p_rarch->bsv_movie_state.eof_exit = true;
- #endif
- break;
- case RA_OPT_VERSION:
- retroarch_print_version();
- exit(0);
- case RA_OPT_LOG_FILE:
- /* Enable 'log to file' */
- configuration_set_bool(p_rarch->configuration_settings,
- p_rarch->configuration_settings->bools.log_to_file, true);
- retroarch_override_setting_set(
- RARCH_OVERRIDE_SETTING_LOG_TO_FILE, NULL);
- /* Cache log file path override */
- rarch_log_file_set_override(optarg);
- break;
- case 'h':
- #ifdef HAVE_CONFIGFILE
- case 'c':
- case RA_OPT_APPENDCONFIG:
- #endif
- case 's':
- case 'S':
- break; /* Handled in the first pass */
- case '?':
- retroarch_print_help(argv[0]);
- retroarch_fail(1, "retroarch_parse_input()");
- case RA_OPT_ACCESSIBILITY:
- #ifdef HAVE_ACCESSIBILITY
- p_rarch->accessibility_enabled = true;
- #endif
- break;
- case RA_OPT_LOAD_MENU_ON_ERROR:
- global->cli_load_menu_on_error = true;
- break;
- default:
- RARCH_ERR("%s\n", msg_hash_to_str(MSG_ERROR_PARSING_ARGUMENTS));
- retroarch_fail(1, "retroarch_parse_input()");
- }
- }
- }
- if (verbosity_is_enabled())
- rarch_log_file_init(
- p_rarch->configuration_settings->bools.log_to_file,
- p_rarch->configuration_settings->bools.log_to_file_timestamp,
- p_rarch->configuration_settings->paths.log_dir);
- #ifdef HAVE_GIT_VERSION
- RARCH_LOG("RetroArch %s (Git %s)\n",
- PACKAGE_VERSION, retroarch_git_version);
- #endif
- if (explicit_menu)
- {
- if (optind < argc)
- {
- RARCH_ERR("--menu was used, but content file was passed as well.\n");
- retroarch_fail(1, "retroarch_parse_input()");
- }
- #ifdef HAVE_DYNAMIC
- else
- {
- /* Allow stray -L arguments to go through to workaround cases
- * where it's used as "config file".
- *
- * This seems to still be the case for Android, which
- * should be properly fixed. */
- retroarch_set_current_core_type(CORE_TYPE_DUMMY, false);
- }
- #endif
- }
- if (optind < argc)
- {
- bool subsystem_path_is_empty = path_is_empty(RARCH_PATH_SUBSYSTEM);
- /* We requested explicit ROM, so use PLAIN core type. */
- retroarch_set_current_core_type(CORE_TYPE_PLAIN, false);
- if (subsystem_path_is_empty)
- path_set(RARCH_PATH_NAMES, (const char*)argv[optind]);
- else
- path_set_special(argv + optind, argc - optind);
- /* Register that content has been set via the
- * command line interface */
- cli_content_set = true;
- }
- /* Check whether a core has been set via the
- * command line interface */
- cli_core_set = (p_rarch->current_core_type != CORE_TYPE_DUMMY);
- /* Update global 'content launched from command
- * line' status flag */
- global->launched_from_cli = cli_active && (cli_core_set || cli_content_set);
- /* Copy SRM/state dirs used, so they can be reused on reentrancy. */
- if (retroarch_override_setting_is_set(RARCH_OVERRIDE_SETTING_SAVE_PATH, NULL) &&
- path_is_directory(global->name.savefile))
- dir_set(RARCH_DIR_SAVEFILE, global->name.savefile);
- if (retroarch_override_setting_is_set(RARCH_OVERRIDE_SETTING_STATE_PATH, NULL) &&
- path_is_directory(global->name.savestate))
- dir_set(RARCH_DIR_SAVESTATE, global->name.savestate);
- }
- static bool retroarch_validate_per_core_options(char *s,
- size_t len, bool mkdir,
- const char *core_name, const char *game_name)
- {
- char config_directory[PATH_MAX_LENGTH];
- config_directory[0] = '\0';
- if (!s ||
- (len < 1) ||
- string_is_empty(core_name) ||
- string_is_empty(game_name))
- return false;
- fill_pathname_application_special(config_directory,
- sizeof(config_directory), APPLICATION_SPECIAL_DIRECTORY_CONFIG);
- fill_pathname_join_special_ext(s,
- config_directory, core_name, game_name,
- ".opt", len);
- /* No need to make a directory if file already exists... */
- if (mkdir && !path_is_valid(s))
- {
- char new_path[PATH_MAX_LENGTH];
- new_path[0] = '\0';
- fill_pathname_join(new_path,
- config_directory, core_name, sizeof(new_path));
- if (!path_is_directory(new_path))
- path_mkdir(new_path);
- }
- return true;
- }
- static bool retroarch_validate_game_options(char *s, size_t len, bool mkdir)
- {
- struct rarch_state *p_rarch = &rarch_st;
- const char *core_name = p_rarch->runloop_system.info.library_name;
- const char *game_name = path_basename(path_get(RARCH_PATH_BASENAME));
- return retroarch_validate_per_core_options(s, len, mkdir,
- core_name, game_name);
- }
- static bool retroarch_validate_folder_options(char *s, size_t len, bool mkdir)
- {
- struct rarch_state *p_rarch = &rarch_st;
- const char *core_name = p_rarch->runloop_system.info.library_name;
- const char *game_path = path_get(RARCH_PATH_BASENAME);
- char folder_name[PATH_MAX_LENGTH];
- folder_name[0] = '\0';
- if (string_is_empty(game_path))
- return false;
- fill_pathname_parent_dir_name(folder_name,
- game_path, sizeof(folder_name));
- return retroarch_validate_per_core_options(s, len, mkdir,
- core_name, folder_name);
- }
- /* Validates CPU features for given processor architecture.
- * Make sure we haven't compiled for something we cannot run.
- * Ideally, code would get swapped out depending on CPU support,
- * but this will do for now. */
- static void retroarch_validate_cpu_features(void)
- {
- uint64_t cpu = cpu_features_get();
- (void)cpu;
- #ifdef __MMX__
- if (!(cpu & RETRO_SIMD_MMX))
- FAIL_CPU("MMX");
- #endif
- #ifdef __SSE__
- if (!(cpu & RETRO_SIMD_SSE))
- FAIL_CPU("SSE");
- #endif
- #ifdef __SSE2__
- if (!(cpu & RETRO_SIMD_SSE2))
- FAIL_CPU("SSE2");
- #endif
- #ifdef __AVX__
- if (!(cpu & RETRO_SIMD_AVX))
- FAIL_CPU("AVX");
- #endif
- }
- /**
- * retroarch_main_init:
- * @argc : Count of (commandline) arguments.
- * @argv : (Commandline) arguments.
- *
- * Initializes the program.
- *
- * Returns: true on success, otherwise false if there was an error.
- **/
- bool retroarch_main_init(int argc, char *argv[])
- {
- #if defined(DEBUG) && defined(HAVE_DRMINGW)
- char log_file_name[128];
- #endif
- bool init_failed = false;
- struct rarch_state *p_rarch = &rarch_st;
- global_t *global = &p_rarch->g_extern;
- p_rarch->osk_idx = OSK_LOWERCASE_LATIN;
- p_rarch->video_driver_active = true;
- p_rarch->audio_driver_active = true;
- if (setjmp(p_rarch->error_sjlj_context) > 0)
- {
- RARCH_ERR("%s: \"%s\"\n",
- msg_hash_to_str(MSG_FATAL_ERROR_RECEIVED_IN), p_rarch->error_string);
- goto error;
- }
- p_rarch->rarch_error_on_init = true;
- /* Have to initialise non-file logging once at the start... */
- retro_main_log_file_init(NULL, false);
- retroarch_parse_input_and_config(p_rarch, &p_rarch->g_extern, argc, argv);
- #ifdef HAVE_ACCESSIBILITY
- if (is_accessibility_enabled(p_rarch))
- {
- /* State that the narrator is on, and also include the first menu
- item we're on at startup. */
- accessibility_speak_priority(p_rarch,
- "RetroArch accessibility on. Main Menu Load Core.",
- 10);
- }
- #endif
- if (verbosity_is_enabled())
- {
- {
- char str_output[256];
- const char *cpu_model = NULL;
- str_output[0] = '\0';
- cpu_model = frontend_driver_get_cpu_model_name();
- strlcat(str_output,
- "=== Build =======================================\n",
- sizeof(str_output));
- if (!string_is_empty(cpu_model))
- {
- strlcat(str_output, FILE_PATH_LOG_INFO " CPU Model Name: ", sizeof(str_output));
- strlcat(str_output, cpu_model, sizeof(str_output));
- strlcat(str_output, "\n", sizeof(str_output));
- }
- RARCH_LOG_OUTPUT(str_output);
- }
- {
- char str_output[256];
- char str[128];
- str[0] = str_output[0] = '\0';
- retroarch_get_capabilities(RARCH_CAPABILITIES_CPU, str, sizeof(str));
- strlcat(str_output, msg_hash_to_str(MSG_CAPABILITIES),
- sizeof(str_output));
- strlcat(str_output, ": ", sizeof(str_output));
- strlcat(str_output, str, sizeof(str_output));
- strlcat(str_output, "\n" FILE_PATH_LOG_INFO " Built: " __DATE__ "\n" FILE_PATH_LOG_INFO " Version: " PACKAGE_VERSION "\n", sizeof(str_output));
- #ifdef HAVE_GIT_VERSION
- strlcat(str_output, FILE_PATH_LOG_INFO " Git: ", sizeof(str_output));
- strlcat(str_output, retroarch_git_version, sizeof(str_output));
- strlcat(str_output, "\n", sizeof(str_output));
- #endif
- strlcat(str_output, FILE_PATH_LOG_INFO " =================================================\n", sizeof(str_output));
- RARCH_LOG_OUTPUT(str_output);
- }
- }
- #if defined(DEBUG) && defined(HAVE_DRMINGW)
- RARCH_LOG_OUTPUT("Initializing Dr.MingW Exception handler\n");
- fill_str_dated_filename(log_file_name, "crash",
- "log", sizeof(log_file_name));
- ExcHndlInit();
- ExcHndlSetLogFileNameA(log_file_name);
- #endif
- retroarch_validate_cpu_features();
- retroarch_init_task_queue();
- {
- const char *fullpath = path_get(RARCH_PATH_CONTENT);
- if (!string_is_empty(fullpath))
- {
- settings_t *settings = p_rarch->configuration_settings;
- enum rarch_content_type cont_type = path_is_media_type(fullpath);
- #ifdef HAVE_IMAGEVIEWER
- bool builtin_imageviewer = settings ? settings->bools.multimedia_builtin_imageviewer_enable : false;
- #endif
- bool builtin_mediaplayer = settings ? settings->bools.multimedia_builtin_mediaplayer_enable : false;
- switch (cont_type)
- {
- case RARCH_CONTENT_MOVIE:
- case RARCH_CONTENT_MUSIC:
- if (builtin_mediaplayer)
- {
- /* TODO/FIXME - it needs to become possible to
- * switch between FFmpeg and MPV at runtime */
- #if defined(HAVE_MPV)
- retroarch_override_setting_set(RARCH_OVERRIDE_SETTING_LIBRETRO, NULL);
- retroarch_set_current_core_type(CORE_TYPE_MPV, false);
- #elif defined(HAVE_FFMPEG)
- retroarch_override_setting_set(RARCH_OVERRIDE_SETTING_LIBRETRO, NULL);
- retroarch_set_current_core_type(CORE_TYPE_FFMPEG, false);
- #endif
- }
- break;
- #ifdef HAVE_IMAGEVIEWER
- case RARCH_CONTENT_IMAGE:
- if (builtin_imageviewer)
- {
- retroarch_override_setting_set(RARCH_OVERRIDE_SETTING_LIBRETRO, NULL);
- retroarch_set_current_core_type(CORE_TYPE_IMAGEVIEWER, false);
- }
- break;
- #endif
- #ifdef HAVE_GONG
- case RARCH_CONTENT_GONG:
- retroarch_override_setting_set(RARCH_OVERRIDE_SETTING_LIBRETRO, NULL);
- retroarch_set_current_core_type(CORE_TYPE_GONG, false);
- break;
- #endif
- default:
- break;
- }
- }
- }
- /* Pre-initialize all drivers
- * Attempts to find a default driver for
- * all driver types.
- */
- audio_driver_find_driver(p_rarch);
- video_driver_find_driver(p_rarch);
- input_driver_find_driver(p_rarch);
- camera_driver_find_driver(p_rarch);
- bluetooth_driver_ctl(RARCH_BLUETOOTH_CTL_FIND_DRIVER, NULL);
- wifi_driver_ctl(RARCH_WIFI_CTL_FIND_DRIVER, NULL);
- find_location_driver(p_rarch);
- #ifdef HAVE_MENU
- menu_driver_ctl(RARCH_MENU_CTL_FIND_DRIVER, NULL);
- #endif
- /* Attempt to initialize core */
- if (p_rarch->has_set_core)
- {
- p_rarch->has_set_core = false;
- if (!command_event(CMD_EVENT_CORE_INIT,
- &p_rarch->explicit_current_core_type))
- init_failed = true;
- }
- else if (!command_event(CMD_EVENT_CORE_INIT,
- &p_rarch->current_core_type))
- init_failed = true;
- /* Handle core initialization failure */
- if (init_failed)
- {
- /* Check if menu was active prior to core initialization */
- if ( !global->launched_from_cli
- || global->cli_load_menu_on_error
- #ifdef HAVE_MENU
- || p_rarch->menu_driver_alive
- #endif
- )
- {
- /* Attempt initializing dummy core */
- p_rarch->current_core_type = CORE_TYPE_DUMMY;
- if (!command_event(CMD_EVENT_CORE_INIT, &p_rarch->current_core_type))
- goto error;
- }
- else /* Fall back to regular error handling */
- goto error;
- }
- #ifdef HAVE_CHEATS
- cheat_manager_state_free();
- command_event_init_cheats(p_rarch->configuration_settings, p_rarch);
- #endif
- drivers_init(p_rarch, DRIVERS_CMD_ALL);
- #ifdef HAVE_COMMAND
- input_driver_deinit_command(p_rarch);
- input_driver_init_command(p_rarch);
- #endif
- #ifdef HAVE_NETWORKGAMEPAD
- if (p_rarch->input_driver_remote)
- input_remote_free(p_rarch->input_driver_remote,
- p_rarch->input_driver_max_users);
- p_rarch->input_driver_remote = NULL;
- if (p_rarch->configuration_settings->bools.network_remote_enable)
- p_rarch->input_driver_remote = input_driver_init_remote(
- p_rarch->configuration_settings,
- p_rarch->input_driver_max_users);
- #endif
- if (p_rarch->input_driver_mapper)
- free(p_rarch->input_driver_mapper);
- p_rarch->input_driver_mapper = NULL;
- if (p_rarch->configuration_settings->bools.input_remap_binds_enable)
- p_rarch->input_driver_mapper = (input_mapper_t*)calloc(1, sizeof(*p_rarch->input_driver_mapper));
- #ifdef HAVE_REWIND
- command_event(CMD_EVENT_REWIND_INIT, NULL);
- #endif
- command_event_init_controllers(p_rarch);
- if (!string_is_empty(global->record.path))
- command_event(CMD_EVENT_RECORD_INIT, NULL);
- path_init_savefile(p_rarch);
- command_event(CMD_EVENT_SET_PER_GAME_RESOLUTION, NULL);
- p_rarch->rarch_error_on_init = false;
- p_rarch->rarch_is_inited = true;
- #ifdef HAVE_DISCORD
- if (command_event(CMD_EVENT_DISCORD_INIT, NULL))
- discord_is_inited = true;
- if (discord_is_inited)
- {
- discord_userdata_t userdata;
- userdata.status = DISCORD_PRESENCE_MENU;
- command_event(CMD_EVENT_DISCORD_UPDATE, &userdata);
- }
- #endif
- #if defined(HAVE_AUDIOMIXER)
- audio_driver_load_system_sounds();
- #endif
- return true;
- error:
- command_event(CMD_EVENT_CORE_DEINIT, NULL);
- p_rarch->rarch_is_inited = false;
- return false;
- }
- #if 0
- static bool retroarch_is_on_main_thread(void)
- {
- #ifdef HAVE_THREAD_STORAGE
- struct rarch_state *p_rarch = &rarch_st;
- if (sthread_tls_get(&p_rarch->rarch_tls) != MAGIC_POINTER)
- return false;
- #endif
- return true;
- }
- #endif
- #ifdef HAVE_MENU
- /* This callback gets triggered by the keyboard whenever
- * we press or release a keyboard key. When a keyboard
- * key is being pressed down, 'down' will be true. If it
- * is being released, 'down' will be false.
- */
- static void menu_input_key_event(bool down, unsigned keycode,
- uint32_t character, uint16_t mod)
- {
- struct rarch_state *p_rarch = &rarch_st;
- enum retro_key key = (enum retro_key)keycode;
- if (key == RETROK_UNKNOWN)
- {
- unsigned i;
- for (i = 0; i < RETROK_LAST; i++)
- p_rarch->menu_keyboard_key_state[i] =
- (p_rarch->menu_keyboard_key_state[(enum retro_key)i] & 1) << 1;
- }
- else
- p_rarch->menu_keyboard_key_state[key] =
- ((p_rarch->menu_keyboard_key_state[key] & 1) << 1) | down;
- }
- /* Gets called when we want to toggle the menu.
- * If the menu is already running, it will be turned off.
- * If the menu is off, then the menu will be started.
- */
- static void menu_driver_toggle(
- struct rarch_state *p_rarch,
- menu_handle_t *menu,
- settings_t *settings,
- retro_keyboard_event_t *key_event,
- retro_keyboard_event_t *frontend_key_event,
- bool on)
- {
- /* TODO/FIXME - retroarch_main_quit calls menu_driver_toggle -
- * we might have to redesign this to avoid EXXC_BAD_ACCESS errors
- * on OSX - for now we work around this by checking if the settings
- * struct is NULL
- */
- bool pause_libretro = settings ?
- settings->bools.menu_pause_libretro : false;
- #ifdef HAVE_AUDIOMIXER
- bool audio_enable_menu = settings ? settings->bools.audio_enable_menu : false;
- #if 0
- bool audio_enable_menu_bgm = settings ? settings->bools.audio_enable_menu_bgm : false;
- #endif
- #endif
- bool runloop_shutdown_initiated = p_rarch->runloop_shutdown_initiated;
- if (menu->driver_ctx && menu->driver_ctx->toggle)
- menu->driver_ctx->toggle(menu->userdata, on);
- p_rarch->menu_driver_alive = on;
- /* Apply any required menu pointer input inhibits
- * (i.e. prevent phantom input when using an overlay
- * to toggle the menu on) */
- menu_input_driver_toggle(p_rarch,
- &p_rarch->menu_input_state, settings, on);
- if (p_rarch->menu_driver_alive)
- {
- bool refresh = false;
- #ifdef WIIU
- /* Enable burn-in protection menu is running */
- IMEnableDim();
- #endif
- menu_entries_ctl(MENU_ENTRIES_CTL_SET_REFRESH, &refresh);
- /* Menu should always run with vsync on. */
- if (p_rarch->current_video->set_nonblock_state)
- p_rarch->current_video->set_nonblock_state(p_rarch->video_driver_data, false,
- video_driver_test_all_flags(GFX_CTX_FLAGS_ADAPTIVE_VSYNC) &&
- settings->bools.video_adaptive_vsync,
- settings->uints.video_swap_interval
- );
- /* Stop all rumbling before entering the menu. */
- command_event(CMD_EVENT_RUMBLE_STOP, NULL);
- if (pause_libretro && !audio_enable_menu)
- command_event(CMD_EVENT_AUDIO_STOP, NULL);
- #if 0
- if (audio_enable_menu && audio_enable_menu_bgm)
- audio_driver_mixer_play_menu_sound_looped(AUDIO_MIXER_SYSTEM_SLOT_BGM);
- #endif
- /* Override keyboard callback to redirect to menu instead.
- * We'll use this later for something ... */
- if (key_event && frontend_key_event)
- {
- *frontend_key_event = *key_event;
- *key_event = menu_input_key_event;
- p_rarch->runloop_frame_time_last = 0;
- }
- }
- else
- {
- #ifdef WIIU
- /* Disable burn-in protection while core is running; this is needed
- * because HID inputs don't count for the purpose of Wii U
- * power-saving. */
- IMDisableDim();
- #endif
- if (!runloop_shutdown_initiated)
- driver_set_nonblock_state();
- if (pause_libretro && !audio_enable_menu)
- command_event(CMD_EVENT_AUDIO_START, NULL);
- #if 0
- if (audio_enable_menu && audio_enable_menu_bgm)
- audio_driver_mixer_stop_stream(AUDIO_MIXER_SYSTEM_SLOT_BGM);
- #endif
- /* Restore libretro keyboard callback. */
- if (key_event && frontend_key_event)
- *key_event = *frontend_key_event;
- }
- }
- #endif
- void retroarch_menu_running(void)
- {
- struct rarch_state *p_rarch = &rarch_st;
- #if defined(HAVE_MENU) || defined(HAVE_OVERLAY)
- settings_t *settings = p_rarch->configuration_settings;
- #endif
- #ifdef HAVE_OVERLAY
- bool input_overlay_hide_in_menu = settings->bools.input_overlay_hide_in_menu;
- #endif
- #ifdef HAVE_AUDIOMIXER
- bool audio_enable_menu = settings->bools.audio_enable_menu;
- bool audio_enable_menu_bgm = settings->bools.audio_enable_menu_bgm;
- #endif
- #ifdef HAVE_MENU
- menu_handle_t *menu = p_rarch->menu_driver_data;
- if (menu)
- menu_driver_toggle(p_rarch, menu, settings,
- &p_rarch->runloop_key_event,
- &p_rarch->runloop_frontend_key_event,
- true);
- /* Prevent stray input (for a single frame) */
- p_rarch->input_driver_flushing_input = 1;
- #ifdef HAVE_AUDIOMIXER
- if (audio_enable_menu && audio_enable_menu_bgm)
- audio_driver_mixer_play_menu_sound_looped(AUDIO_MIXER_SYSTEM_SLOT_BGM);
- #endif
- /* Ensure that game focus mode is disabled when
- * running the menu (note: it is not currently
- * possible for game focus to be enabled at this
- * point, but must safeguard against future changes) */
- if (p_rarch->game_focus_state.enabled)
- {
- enum input_game_focus_cmd_type game_focus_cmd = GAME_FOCUS_CMD_OFF;
- command_event(CMD_EVENT_GAME_FOCUS_TOGGLE, &game_focus_cmd);
- }
- #endif
- #ifdef HAVE_OVERLAY
- if (input_overlay_hide_in_menu)
- command_event(CMD_EVENT_OVERLAY_DEINIT, NULL);
- #endif
- }
- void retroarch_menu_running_finished(bool quit)
- {
- struct rarch_state *p_rarch = &rarch_st;
- #if defined(HAVE_MENU) || defined(HAVE_OVERLAY)
- settings_t *settings = p_rarch->configuration_settings;
- #endif
- #ifdef HAVE_MENU
- menu_handle_t *menu = p_rarch->menu_driver_data;
- if (menu)
- menu_driver_toggle(p_rarch, menu, settings,
- &p_rarch->runloop_key_event,
- &p_rarch->runloop_frontend_key_event,
- false);
- /* Prevent stray input
- * (for a single frame) */
- p_rarch->input_driver_flushing_input = 1;
- #ifdef HAVE_AUDIOMIXER
- if (!quit)
- /* Stop menu background music before we exit the menu */
- if ( settings &&
- settings->bools.audio_enable_menu &&
- settings->bools.audio_enable_menu_bgm
- )
- audio_driver_mixer_stop_stream(AUDIO_MIXER_SYSTEM_SLOT_BGM);
- #endif
- /* Enable game focus mode, if required */
- if (!quit && (p_rarch->current_core_type != CORE_TYPE_DUMMY))
- {
- enum input_auto_game_focus_type auto_game_focus_type = settings ?
- (enum input_auto_game_focus_type)settings->uints.input_auto_game_focus :
- AUTO_GAME_FOCUS_OFF;
- if ((auto_game_focus_type == AUTO_GAME_FOCUS_ON) ||
- ((auto_game_focus_type == AUTO_GAME_FOCUS_DETECT) &&
- p_rarch->game_focus_state.core_requested))
- {
- enum input_game_focus_cmd_type game_focus_cmd = GAME_FOCUS_CMD_ON;
- command_event(CMD_EVENT_GAME_FOCUS_TOGGLE, &game_focus_cmd);
- }
- }
- #endif
- video_driver_set_texture_enable(false, false);
- #ifdef HAVE_OVERLAY
- if (!quit)
- if (settings && settings->bools.input_overlay_hide_in_menu)
- retroarch_overlay_init(p_rarch);
- #endif
- }
- /**
- * rarch_game_specific_options:
- *
- * Returns: true (1) if a game specific core
- * options path has been found,
- * otherwise false (0).
- **/
- static bool rarch_game_specific_options(char **output)
- {
- char game_options_path[PATH_MAX_LENGTH];
- game_options_path[0] ='\0';
- if (!retroarch_validate_game_options(game_options_path,
- sizeof(game_options_path), false) ||
- !path_is_valid(game_options_path))
- return false;
- RARCH_LOG("%s %s\n",
- msg_hash_to_str(MSG_GAME_SPECIFIC_CORE_OPTIONS_FOUND_AT),
- game_options_path);
- *output = strdup(game_options_path);
- return true;
- }
- /**
- * rarch_folder_specific_options:
- *
- * Returns: true (1) if a folder specific core
- * options path has been found,
- * otherwise false (0).
- **/
- static bool rarch_folder_specific_options(char **output)
- {
- char folder_options_path[PATH_MAX_LENGTH];
- folder_options_path[0] ='\0';
- if (!retroarch_validate_folder_options(folder_options_path,
- sizeof(folder_options_path), false) ||
- !path_is_valid(folder_options_path))
- return false;
- RARCH_LOG("%s %s\n",
- msg_hash_to_str(MSG_FOLDER_SPECIFIC_CORE_OPTIONS_FOUND_AT),
- folder_options_path);
- *output = strdup(folder_options_path);
- return true;
- }
- static void runloop_task_msg_queue_push(
- retro_task_t *task, const char *msg,
- unsigned prio, unsigned duration,
- bool flush)
- {
- #if defined(HAVE_GFX_WIDGETS)
- struct rarch_state *p_rarch = &rarch_st;
- bool widgets_active = p_rarch->widgets_active;
- if (widgets_active && task->title && !task->mute)
- {
- RUNLOOP_MSG_QUEUE_LOCK(p_rarch);
- ui_companion_driver_msg_queue_push(p_rarch, msg,
- prio, task ? duration : duration * 60 / 1000, flush);
- #ifdef HAVE_ACCESSIBILITY
- if (is_accessibility_enabled(p_rarch))
- accessibility_speak_priority(p_rarch, (char*)msg, 0);
- #endif
- gfx_widgets_msg_queue_push(
- &p_rarch->dispwidget_st,
- task,
- msg,
- duration,
- NULL,
- (enum message_queue_icon)MESSAGE_QUEUE_CATEGORY_INFO,
- (enum message_queue_category)MESSAGE_QUEUE_ICON_DEFAULT,
- prio,
- flush,
- #ifdef HAVE_MENU
- p_rarch->menu_driver_alive
- #else
- false
- #endif
- );
- RUNLOOP_MSG_QUEUE_UNLOCK(p_rarch);
- }
- else
- #endif
- runloop_msg_queue_push(msg, prio, duration, flush, NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO);
- }
- /* Fetches core options path for current core/content
- * - path: path from which options should be read
- * from/saved to
- * - src_path: in the event that 'path' file does not
- * yet exist, provides source path from which initial
- * options should be extracted
- * */
- static void rarch_init_core_options_path(
- struct rarch_state *p_rarch,
- char *path, size_t len,
- char *src_path, size_t src_len)
- {
- char *game_options_path = NULL;
- char *folder_options_path = NULL;
-
- settings_t *settings = p_rarch->configuration_settings;
- bool game_specific_options = settings->bools.game_specific_options;
- /* Ensure that 'input' strings are null terminated */
- if (len > 0)
- path[0] = '\0';
- if (src_len > 0)
- src_path[0] = '\0';
- /* Check whether game-specific options exist */
- if (game_specific_options &&
- rarch_game_specific_options(&game_options_path))
- {
- /* Notify system that we have a valid core options
- * override */
- path_set(RARCH_PATH_CORE_OPTIONS, game_options_path);
- p_rarch->runloop_game_options_active = true;
- p_rarch->runloop_folder_options_active = false;
- /* Copy options path */
- strlcpy(path, game_options_path, len);
- free(game_options_path);
- }
- /* Check whether folder-specific options exist */
- else if (game_specific_options &&
- rarch_folder_specific_options(&folder_options_path))
- {
- /* Notify system that we have a valid core options
- * override */
- path_set(RARCH_PATH_CORE_OPTIONS, folder_options_path);
- p_rarch->runloop_game_options_active = false;
- p_rarch->runloop_folder_options_active = true;
- /* Copy options path */
- strlcpy(path, folder_options_path, len);
- free(folder_options_path);
- }
- else
- {
- char global_options_path[PATH_MAX_LENGTH];
- char per_core_options_path[PATH_MAX_LENGTH];
- bool per_core_options_exist = false;
- bool per_core_options = !settings->bools.global_core_options;
- const char *path_core_options = settings->paths.path_core_options;
- global_options_path[0] = '\0';
- per_core_options_path[0] = '\0';
- if (per_core_options)
- {
- const char *core_name = p_rarch->runloop_system.info.library_name;
- /* Get core-specific options path
- * > if retroarch_validate_per_core_options() returns
- * false, then per-core options are disabled (due to
- * unknown system errors...) */
- per_core_options = retroarch_validate_per_core_options(
- per_core_options_path, sizeof(per_core_options_path), true,
- core_name, core_name);
- /* If we can use per-core options, check whether an options
- * file already exists */
- if (per_core_options)
- per_core_options_exist = path_is_valid(per_core_options_path);
- }
- /* If not using per-core options, or if a per-core options
- * file does not yet exist, must fetch 'global' options path */
- if (!per_core_options || !per_core_options_exist)
- {
- const char *options_path = path_core_options;
- if (!string_is_empty(options_path))
- strlcpy(global_options_path,
- options_path, sizeof(global_options_path));
- else if (!path_is_empty(RARCH_PATH_CONFIG))
- fill_pathname_resolve_relative(
- global_options_path, path_get(RARCH_PATH_CONFIG),
- FILE_PATH_CORE_OPTIONS_CONFIG, sizeof(global_options_path));
- }
- /* Allocate correct path/src_path strings */
- if (per_core_options)
- {
- strlcpy(path, per_core_options_path, len);
- if (!per_core_options_exist)
- strlcpy(src_path, global_options_path, src_len);
- }
- else
- strlcpy(path, global_options_path, len);
- /* Notify system that we *do not* have a valid core options
- * options override */
- p_rarch->runloop_game_options_active = false;
- p_rarch->runloop_folder_options_active = false;
- }
- }
- static void rarch_init_core_options(
- struct rarch_state *p_rarch,
- const struct retro_core_option_definition *option_defs)
- {
- char options_path[PATH_MAX_LENGTH];
- char src_options_path[PATH_MAX_LENGTH];
- options_path[0] = '\0';
- src_options_path[0] = '\0';
- /* Get core options file path */
- rarch_init_core_options_path(p_rarch,
- options_path, sizeof(options_path),
- src_options_path, sizeof(src_options_path));
- if (!string_is_empty(options_path))
- p_rarch->runloop_core_options =
- core_option_manager_new(options_path, src_options_path, option_defs);
- }
- void retroarch_init_task_queue(void)
- {
- #ifdef HAVE_THREADS
- struct rarch_state *p_rarch = &rarch_st;
- settings_t *settings = p_rarch->configuration_settings;
- bool threaded_enable = settings->bools.threaded_data_runloop_enable;
- #else
- bool threaded_enable = false;
- #endif
- task_queue_deinit();
- task_queue_init(threaded_enable, runloop_task_msg_queue_push);
- }
- bool rarch_ctl(enum rarch_ctl_state state, void *data)
- {
- struct rarch_state *p_rarch = &rarch_st;
- switch(state)
- {
- case RARCH_CTL_HAS_SET_SUBSYSTEMS:
- return (p_rarch->current_core.has_set_subsystems);
- case RARCH_CTL_CORE_IS_RUNNING:
- return p_rarch->runloop_core_running;
- #ifdef HAVE_BSV_MOVIE
- case RARCH_CTL_BSV_MOVIE_IS_INITED:
- return (p_rarch->bsv_movie_state_handle != NULL);
- #endif
- #ifdef HAVE_PATCH
- case RARCH_CTL_IS_PATCH_BLOCKED:
- return p_rarch->rarch_patch_blocked;
- case RARCH_CTL_IS_BPS_PREF:
- return p_rarch->rarch_bps_pref;
- case RARCH_CTL_UNSET_BPS_PREF:
- p_rarch->rarch_bps_pref = false;
- break;
- case RARCH_CTL_IS_UPS_PREF:
- return p_rarch->rarch_ups_pref;
- case RARCH_CTL_UNSET_UPS_PREF:
- p_rarch->rarch_ups_pref = false;
- break;
- case RARCH_CTL_IS_IPS_PREF:
- return p_rarch->rarch_ips_pref;
- case RARCH_CTL_UNSET_IPS_PREF:
- p_rarch->rarch_ips_pref = false;
- break;
- #endif
- case RARCH_CTL_IS_DUMMY_CORE:
- return (p_rarch->current_core_type == CORE_TYPE_DUMMY);
- case RARCH_CTL_IS_CORE_LOADED:
- {
- const char *core_path = (const char*)data;
- const char *core_file = NULL;
- const char *loaded_core_path = NULL;
- const char *loaded_core_file = NULL;
- if (string_is_empty(core_path))
- return false;
- /* Get core file name */
- core_file = path_basename(core_path);
- if (string_is_empty(core_file))
- return false;
- /* Get loaded core file name */
- loaded_core_path = path_get(RARCH_PATH_CORE);
- if (!string_is_empty(loaded_core_path))
- loaded_core_file = path_basename(loaded_core_path);
- /* Check whether specified core and currently
- * loaded core are the same */
- if (!string_is_empty(loaded_core_file) &&
- string_is_equal(core_file, loaded_core_file))
- return true;
- }
- return false;
- case RARCH_CTL_HAS_SET_USERNAME:
- return p_rarch->has_set_username;
- case RARCH_CTL_IS_INITED:
- return p_rarch->rarch_is_inited;
- case RARCH_CTL_MAIN_DEINIT:
- if (!p_rarch->rarch_is_inited)
- return false;
- command_event(CMD_EVENT_NETPLAY_DEINIT, NULL);
- #ifdef HAVE_COMMAND
- input_driver_deinit_command(p_rarch);
- #endif
- #ifdef HAVE_NETWORKGAMEPAD
- if (p_rarch->input_driver_remote)
- input_remote_free(p_rarch->input_driver_remote,
- p_rarch->input_driver_max_users);
- p_rarch->input_driver_remote = NULL;
- #endif
- if (p_rarch->input_driver_mapper)
- free(p_rarch->input_driver_mapper);
- p_rarch->input_driver_mapper = NULL;
- #ifdef HAVE_THREADS
- retroarch_autosave_deinit(p_rarch);
- #endif
- command_event(CMD_EVENT_RECORD_DEINIT, NULL);
- command_event(CMD_EVENT_SAVE_FILES, NULL);
- #ifdef HAVE_REWIND
- command_event(CMD_EVENT_REWIND_DEINIT, NULL);
- #endif
- #ifdef HAVE_CHEATS
- cheat_manager_state_free();
- #endif
- #ifdef HAVE_BSV_MOVIE
- bsv_movie_deinit(p_rarch);
- #endif
- command_event(CMD_EVENT_CORE_DEINIT, NULL);
- content_deinit();
- path_deinit_subsystem(p_rarch);
- path_deinit_savefile();
- p_rarch->rarch_is_inited = false;
- #ifdef HAVE_THREAD_STORAGE
- sthread_tls_delete(&p_rarch->rarch_tls);
- #endif
- break;
- #ifdef HAVE_CONFIGFILE
- case RARCH_CTL_SET_BLOCK_CONFIG_READ:
- p_rarch->rarch_block_config_read = true;
- break;
- case RARCH_CTL_UNSET_BLOCK_CONFIG_READ:
- p_rarch->rarch_block_config_read = false;
- break;
- #endif
- case RARCH_CTL_GET_CORE_OPTION_SIZE:
- {
- unsigned *idx = (unsigned*)data;
- if (!idx)
- return false;
- if (p_rarch->runloop_core_options)
- *idx = (unsigned)p_rarch->runloop_core_options->size;
- else
- *idx = 0;
- }
- break;
- case RARCH_CTL_HAS_CORE_OPTIONS:
- return (p_rarch->runloop_core_options != NULL);
- case RARCH_CTL_CORE_OPTIONS_LIST_GET:
- {
- core_option_manager_t **coreopts = (core_option_manager_t**)data;
- if (!coreopts || !p_rarch->runloop_core_options)
- return false;
- *coreopts = p_rarch->runloop_core_options;
- }
- break;
- #ifdef HAVE_CONFIGFILE
- case RARCH_CTL_IS_OVERRIDES_ACTIVE:
- return p_rarch->runloop_overrides_active;
- case RARCH_CTL_SET_REMAPS_CORE_ACTIVE:
- p_rarch->runloop_remaps_core_active = true;
- break;
- case RARCH_CTL_UNSET_REMAPS_CORE_ACTIVE:
- p_rarch->runloop_remaps_core_active = false;
- break;
- case RARCH_CTL_IS_REMAPS_CORE_ACTIVE:
- return p_rarch->runloop_remaps_core_active;
- case RARCH_CTL_SET_REMAPS_GAME_ACTIVE:
- p_rarch->runloop_remaps_game_active = true;
- break;
- case RARCH_CTL_UNSET_REMAPS_GAME_ACTIVE:
- p_rarch->runloop_remaps_game_active = false;
- break;
- case RARCH_CTL_IS_REMAPS_GAME_ACTIVE:
- return p_rarch->runloop_remaps_game_active;
- case RARCH_CTL_SET_REMAPS_CONTENT_DIR_ACTIVE:
- p_rarch->runloop_remaps_content_dir_active = true;
- break;
- case RARCH_CTL_UNSET_REMAPS_CONTENT_DIR_ACTIVE:
- p_rarch->runloop_remaps_content_dir_active = false;
- break;
- case RARCH_CTL_IS_REMAPS_CONTENT_DIR_ACTIVE:
- return p_rarch->runloop_remaps_content_dir_active;
- #endif
- case RARCH_CTL_SET_MISSING_BIOS:
- p_rarch->runloop_missing_bios = true;
- break;
- case RARCH_CTL_UNSET_MISSING_BIOS:
- p_rarch->runloop_missing_bios = false;
- break;
- case RARCH_CTL_IS_MISSING_BIOS:
- return p_rarch->runloop_missing_bios;
- case RARCH_CTL_IS_GAME_OPTIONS_ACTIVE:
- return p_rarch->runloop_game_options_active;
- case RARCH_CTL_IS_FOLDER_OPTIONS_ACTIVE:
- return p_rarch->runloop_folder_options_active;
- case RARCH_CTL_GET_PERFCNT:
- {
- bool **perfcnt = (bool**)data;
- if (!perfcnt)
- return false;
- *perfcnt = &p_rarch->runloop_perfcnt_enable;
- }
- break;
- case RARCH_CTL_SET_PERFCNT_ENABLE:
- p_rarch->runloop_perfcnt_enable = true;
- break;
- case RARCH_CTL_UNSET_PERFCNT_ENABLE:
- p_rarch->runloop_perfcnt_enable = false;
- break;
- case RARCH_CTL_IS_PERFCNT_ENABLE:
- return p_rarch->runloop_perfcnt_enable;
- case RARCH_CTL_SET_WINDOWED_SCALE:
- {
- unsigned *idx = (unsigned*)data;
- if (!idx)
- return false;
- p_rarch->runloop_pending_windowed_scale = *idx;
- }
- break;
- case RARCH_CTL_STATE_FREE:
- p_rarch->runloop_perfcnt_enable = false;
- p_rarch->runloop_idle = false;
- p_rarch->runloop_paused = false;
- p_rarch->runloop_slowmotion = false;
- #ifdef HAVE_CONFIGFILE
- p_rarch->runloop_overrides_active = false;
- #endif
- p_rarch->runloop_autosave = false;
- retroarch_frame_time_free(p_rarch);
- retroarch_audio_buffer_status_free(p_rarch);
- retroarch_game_focus_free(p_rarch);
- break;
- case RARCH_CTL_IS_IDLE:
- return p_rarch->runloop_idle;
- case RARCH_CTL_SET_IDLE:
- {
- bool *ptr = (bool*)data;
- if (!ptr)
- return false;
- p_rarch->runloop_idle = *ptr;
- }
- break;
- case RARCH_CTL_SET_PAUSED:
- {
- bool *ptr = (bool*)data;
- if (!ptr)
- return false;
- p_rarch->runloop_paused = *ptr;
- }
- break;
- case RARCH_CTL_IS_PAUSED:
- return p_rarch->runloop_paused;
- case RARCH_CTL_SET_SHUTDOWN:
- p_rarch->runloop_shutdown_initiated = true;
- break;
- case RARCH_CTL_CORE_OPTION_PREV:
- /*
- * Get previous value for core option specified by @idx.
- * Options wrap around.
- */
- {
- unsigned *idx = (unsigned*)data;
- if (!idx || !p_rarch->runloop_core_options)
- return false;
- core_option_manager_adjust_val(p_rarch->runloop_core_options, *idx, -1);
- }
- break;
- case RARCH_CTL_CORE_OPTION_NEXT:
- /*
- * Get next value for core option specified by @idx.
- * Options wrap around.
- */
- {
- unsigned* idx = (unsigned*)data;
- if (!idx || !p_rarch->runloop_core_options)
- return false;
- core_option_manager_adjust_val(p_rarch->runloop_core_options, *idx, 1);
- }
- break;
- case RARCH_CTL_NONE:
- default:
- return false;
- }
- return true;
- }
- static void retroarch_deinit_core_options(struct rarch_state *p_rarch)
- {
- /* Check whether game-specific options file is being used */
- if (!path_is_empty(RARCH_PATH_CORE_OPTIONS))
- {
- const char *path = path_get(RARCH_PATH_CORE_OPTIONS);
- config_file_t *conf_tmp = NULL;
- /* We only need to save configuration settings for
- * the current core
- * > If game-specific options file exists, have
- * to read it (to ensure file only gets written
- * if config values change)
- * > Otherwise, create a new, empty config_file_t
- * object */
- if (path_is_valid(path))
- conf_tmp = config_file_new_from_path_to_string(path);
- if (!conf_tmp)
- conf_tmp = config_file_new_alloc();
- if (conf_tmp)
- {
- core_option_manager_flush(
- conf_tmp,
- p_rarch->runloop_core_options);
- RARCH_LOG("[Core Options]: Saved %s-specific core options to \"%s\"\n",
- p_rarch->runloop_game_options_active ? "game" : "folder", path);
- config_file_write(conf_tmp, path, true);
- config_file_free(conf_tmp);
- conf_tmp = NULL;
- }
- path_clear(RARCH_PATH_CORE_OPTIONS);
- }
- else
- {
- const char *path = p_rarch->runloop_core_options->conf_path;
- core_option_manager_flush(
- p_rarch->runloop_core_options->conf,
- p_rarch->runloop_core_options);
- RARCH_LOG("[Core Options]: Saved core options file to \"%s\"\n", path);
- config_file_write(p_rarch->runloop_core_options->conf, path, true);
- }
- p_rarch->runloop_game_options_active = false;
- p_rarch->runloop_folder_options_active = false;
- if (p_rarch->runloop_core_options)
- core_option_manager_free(p_rarch->runloop_core_options);
- p_rarch->runloop_core_options = NULL;
- }
- static void retroarch_init_core_variables(
- struct rarch_state *p_rarch,
- const struct retro_variable *vars)
- {
- char options_path[PATH_MAX_LENGTH];
- char src_options_path[PATH_MAX_LENGTH];
- options_path[0] = '\0';
- src_options_path[0] = '\0';
- /* Get core options file path */
- rarch_init_core_options_path(p_rarch,
- options_path, sizeof(options_path),
- src_options_path, sizeof(src_options_path));
- if (!string_is_empty(options_path))
- p_rarch->runloop_core_options =
- core_option_manager_new_vars(options_path, src_options_path, vars);
- }
- bool retroarch_is_forced_fullscreen(void)
- {
- struct rarch_state *p_rarch = &rarch_st;
- return p_rarch->rarch_force_fullscreen;
- }
- bool retroarch_is_switching_display_mode(void)
- {
- struct rarch_state *p_rarch = &rarch_st;
- return p_rarch->rarch_is_switching_display_mode;
- }
- #if defined(HAVE_CG) || defined(HAVE_GLSL) || defined(HAVE_SLANG) || defined(HAVE_HLSL)
- static bool retroarch_load_shader_preset_internal(
- struct rarch_state *p_rarch,
- const char *shader_directory,
- const char *core_name,
- const char *special_name)
- {
- unsigned i;
- char shader_path[PATH_MAX_LENGTH];
- static enum rarch_shader_type types[] =
- {
- /* Shader preset priority, highest to lowest
- * only important for video drivers with multiple shader backends */
- RARCH_SHADER_GLSL, RARCH_SHADER_SLANG, RARCH_SHADER_CG, RARCH_SHADER_HLSL
- };
- for (i = 0; i < ARRAY_SIZE(types); i++)
- {
- if (!video_shader_is_supported(types[i]))
- continue;
- /* Concatenate strings into full paths */
- if (!string_is_empty(core_name))
- fill_pathname_join_special_ext(shader_path,
- shader_directory, core_name,
- special_name,
- video_shader_get_preset_extension(types[i]),
- sizeof(shader_path));
- else
- {
- if (string_is_empty(special_name))
- break;
- fill_pathname_join(shader_path, shader_directory,
- special_name, sizeof(shader_path));
- strlcat(shader_path,
- video_shader_get_preset_extension(types[i]),
- sizeof(shader_path));
- }
- if (!path_is_valid(shader_path))
- continue;
- /* Shader preset exists, load it. */
- RARCH_LOG("[Shaders]: Specific shader preset found at %s.\n",
- shader_path);
- strlcpy(
- p_rarch->runtime_shader_preset,
- shader_path,
- sizeof(p_rarch->runtime_shader_preset));
- return true;
- }
- return false;
- }
- /**
- * retroarch_load_shader_preset:
- *
- * Tries to load a supported core-, game-, folder-specific or global
- * shader preset from its respective location:
- *
- * global: $CONFIG_DIR/global.$PRESET_EXT
- * core-specific: $CONFIG_DIR/$CORE_NAME/$CORE_NAME.$PRESET_EXT
- * folder-specific: $CONFIG_DIR/$CORE_NAME/$FOLDER_NAME.$PRESET_EXT
- * game-specific: $CONFIG_DIR/$CORE_NAME/$GAME_NAME.$PRESET_EXT
- *
- * $CONFIG_DIR is expected to be Menu Config directory, or failing that, the
- * directory where retroarch.cfg is stored.
- *
- * For compatibility purposes with versions 1.8.7 and older, the presets
- * subdirectory on the Video Shader path is used as a fallback directory.
- *
- * Note: Uses video_shader_is_supported() which only works after
- * context driver initialization.
- *
- * Returns: false if there was an error or no action was performed.
- */
- static bool retroarch_load_shader_preset(struct rarch_state *p_rarch)
- {
- settings_t *settings = p_rarch->configuration_settings;
- const char *video_shader_directory = settings->paths.directory_video_shader;
- const char *menu_config_directory = settings->paths.directory_menu_config;
- const char *core_name =
- p_rarch->runloop_system.info.library_name;
- const char *rarch_path_basename = path_get(RARCH_PATH_BASENAME);
- const char *game_name = path_basename(rarch_path_basename);
- const char *dirs[3] = {0};
- size_t i = 0;
- bool ret = false;
- char content_dir_name[PATH_MAX_LENGTH];
- char config_file_directory[PATH_MAX_LENGTH];
- char old_presets_directory[PATH_MAX_LENGTH];
- content_dir_name[0] = '\0';
- config_file_directory[0] = '\0';
- old_presets_directory[0] = '\0';
- if (!string_is_empty(rarch_path_basename))
- fill_pathname_parent_dir_name(content_dir_name,
- rarch_path_basename, sizeof(content_dir_name));
- config_file_directory[0] = '\0';
- if (!path_is_empty(RARCH_PATH_CONFIG))
- fill_pathname_basedir(config_file_directory,
- path_get(RARCH_PATH_CONFIG), sizeof(config_file_directory));
- old_presets_directory[0] = '\0';
- if (!string_is_empty(video_shader_directory))
- fill_pathname_join(old_presets_directory,
- video_shader_directory, "presets", sizeof(old_presets_directory));
- dirs[0] = menu_config_directory;
- dirs[1] = config_file_directory;
- dirs[2] = old_presets_directory;
- for (i = 0; i < ARRAY_SIZE(dirs); i++)
- {
- if (string_is_empty(dirs[i]))
- continue;
- #ifdef DEBUG
- RARCH_LOG("[Shaders]: preset directory: %s\n", dirs[i]);
- #endif
- ret = retroarch_load_shader_preset_internal(p_rarch,
- dirs[i], core_name,
- game_name);
- if (ret)
- {
- #ifdef DEBUG
- RARCH_LOG("[Shaders]: game-specific shader preset found.\n");
- #endif
- break;
- }
- ret = retroarch_load_shader_preset_internal(p_rarch,
- dirs[i], core_name,
- content_dir_name);
- if (ret)
- {
- #ifdef DEBUG
- RARCH_LOG("[Shaders]: folder-specific shader preset found.\n");
- #endif
- break;
- }
- ret = retroarch_load_shader_preset_internal(p_rarch,
- dirs[i], core_name,
- core_name);
- if (ret)
- {
- #ifdef DEBUG
- RARCH_LOG("[Shaders]: core-specific shader preset found.\n");
- #endif
- break;
- }
- ret = retroarch_load_shader_preset_internal(p_rarch,
- dirs[i], NULL,
- "global");
- if (ret)
- {
- #ifdef DEBUG
- RARCH_LOG("[Shaders]: global shader preset found.\n");
- #endif
- break;
- }
- }
- return ret;
- }
- #endif
- /* get the name of the current shader preset */
- const char *retroarch_get_shader_preset(void)
- {
- #if defined(HAVE_CG) || defined(HAVE_GLSL) || defined(HAVE_SLANG) || defined(HAVE_HLSL)
- struct rarch_state *p_rarch = &rarch_st;
- settings_t *settings = p_rarch->configuration_settings;
- const char *core_name = p_rarch->runloop_system.info.library_name;
- bool video_shader_enable = settings->bools.video_shader_enable;
- unsigned video_shader_delay = settings->uints.video_shader_delay;
- bool auto_shaders_enable = settings->bools.auto_shaders_enable;
- bool cli_shader_disable = p_rarch->cli_shader_disable;
- if (!video_shader_enable)
- return NULL;
- if (video_shader_delay && !p_rarch->shader_delay_timer.timer_end)
- return NULL;
- /* disallow loading auto-shaders when no core is loaded */
- if (string_is_empty(core_name))
- return NULL;
- if (!string_is_empty(p_rarch->runtime_shader_preset))
- return p_rarch->runtime_shader_preset;
- /* load auto-shader once, --set-shader works like a global auto-shader */
- if (p_rarch->shader_presets_need_reload && !cli_shader_disable)
- {
- p_rarch->shader_presets_need_reload = false;
- if (video_shader_is_supported(video_shader_parse_type(p_rarch->cli_shader)))
- strlcpy(p_rarch->runtime_shader_preset,
- p_rarch->cli_shader,
- sizeof(p_rarch->runtime_shader_preset));
- else
- if (auto_shaders_enable) /* sets runtime_shader_preset */
- retroarch_load_shader_preset(p_rarch);
- return p_rarch->runtime_shader_preset;
- }
- #endif
- return NULL;
- }
- bool retroarch_override_setting_is_set(
- enum rarch_override_setting enum_idx, void *data)
- {
- struct rarch_state *p_rarch = &rarch_st;
- switch (enum_idx)
- {
- case RARCH_OVERRIDE_SETTING_LIBRETRO_DEVICE:
- {
- unsigned *val = (unsigned*)data;
- if (val)
- {
- unsigned bit = *val;
- return BIT256_GET(p_rarch->has_set_libretro_device, bit);
- }
- }
- break;
- case RARCH_OVERRIDE_SETTING_VERBOSITY:
- return p_rarch->has_set_verbosity;
- case RARCH_OVERRIDE_SETTING_LIBRETRO:
- return p_rarch->has_set_libretro;
- case RARCH_OVERRIDE_SETTING_LIBRETRO_DIRECTORY:
- return p_rarch->has_set_libretro_directory;
- case RARCH_OVERRIDE_SETTING_SAVE_PATH:
- return p_rarch->has_set_save_path;
- case RARCH_OVERRIDE_SETTING_STATE_PATH:
- return p_rarch->has_set_state_path;
- #ifdef HAVE_NETWORKING
- case RARCH_OVERRIDE_SETTING_NETPLAY_MODE:
- return p_rarch->has_set_netplay_mode;
- case RARCH_OVERRIDE_SETTING_NETPLAY_IP_ADDRESS:
- return p_rarch->has_set_netplay_ip_address;
- case RARCH_OVERRIDE_SETTING_NETPLAY_IP_PORT:
- return p_rarch->has_set_netplay_ip_port;
- case RARCH_OVERRIDE_SETTING_NETPLAY_STATELESS_MODE:
- return p_rarch->has_set_netplay_stateless_mode;
- case RARCH_OVERRIDE_SETTING_NETPLAY_CHECK_FRAMES:
- return p_rarch->has_set_netplay_check_frames;
- #endif
- #ifdef HAVE_PATCH
- case RARCH_OVERRIDE_SETTING_UPS_PREF:
- return p_rarch->has_set_ups_pref;
- case RARCH_OVERRIDE_SETTING_BPS_PREF:
- return p_rarch->has_set_bps_pref;
- case RARCH_OVERRIDE_SETTING_IPS_PREF:
- return p_rarch->has_set_ips_pref;
- #endif
- case RARCH_OVERRIDE_SETTING_LOG_TO_FILE:
- return p_rarch->has_set_log_to_file;
- case RARCH_OVERRIDE_SETTING_NONE:
- default:
- break;
- }
- return false;
- }
- int retroarch_get_capabilities(enum rarch_capabilities type,
- char *s, size_t len)
- {
- switch (type)
- {
- case RARCH_CAPABILITIES_CPU:
- {
- uint64_t cpu = cpu_features_get();
- if (cpu & RETRO_SIMD_MMX)
- strlcat(s, " MMX", len);
- if (cpu & RETRO_SIMD_MMXEXT)
- strlcat(s, " MMXEXT", len);
- if (cpu & RETRO_SIMD_SSE)
- strlcat(s, " SSE", len);
- if (cpu & RETRO_SIMD_SSE2)
- strlcat(s, " SSE2", len);
- if (cpu & RETRO_SIMD_SSE3)
- strlcat(s, " SSE3", len);
- if (cpu & RETRO_SIMD_SSSE3)
- strlcat(s, " SSSE3", len);
- if (cpu & RETRO_SIMD_SSE4)
- strlcat(s, " SSE4", len);
- if (cpu & RETRO_SIMD_SSE42)
- strlcat(s, " SSE4.2", len);
- if (cpu & RETRO_SIMD_AES)
- strlcat(s, " AES", len);
- if (cpu & RETRO_SIMD_AVX)
- strlcat(s, " AVX", len);
- if (cpu & RETRO_SIMD_AVX2)
- strlcat(s, " AVX2", len);
- if (cpu & RETRO_SIMD_NEON)
- strlcat(s, " NEON", len);
- if (cpu & RETRO_SIMD_VFPV3)
- strlcat(s, " VFPv3", len);
- if (cpu & RETRO_SIMD_VFPV4)
- strlcat(s, " VFPv4", len);
- if (cpu & RETRO_SIMD_VMX)
- strlcat(s, " VMX", len);
- if (cpu & RETRO_SIMD_VMX128)
- strlcat(s, " VMX128", len);
- if (cpu & RETRO_SIMD_VFPU)
- strlcat(s, " VFPU", len);
- if (cpu & RETRO_SIMD_PS)
- strlcat(s, " PS", len);
- if (cpu & RETRO_SIMD_ASIMD)
- strlcat(s, " ASIMD", len);
- }
- break;
- case RARCH_CAPABILITIES_COMPILER:
- #if defined(_MSC_VER)
- snprintf(s, len, "%s: MSVC (%d) %u-bit",
- msg_hash_to_str(MSG_COMPILER),
- _MSC_VER, (unsigned)
- (CHAR_BIT * sizeof(size_t)));
- #elif defined(__SNC__)
- snprintf(s, len, "%s: SNC (%d) %u-bit",
- msg_hash_to_str(MSG_COMPILER),
- __SN_VER__, (unsigned)(CHAR_BIT * sizeof(size_t)));
- #elif defined(_WIN32) && defined(__GNUC__)
- snprintf(s, len, "%s: MinGW (%d.%d.%d) %u-bit",
- msg_hash_to_str(MSG_COMPILER),
- __GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__, (unsigned)
- (CHAR_BIT * sizeof(size_t)));
- #elif defined(__clang__)
- snprintf(s, len, "%s: Clang/LLVM (%s) %u-bit",
- msg_hash_to_str(MSG_COMPILER),
- __clang_version__, (unsigned)(CHAR_BIT * sizeof(size_t)));
- #elif defined(__GNUC__)
- snprintf(s, len, "%s: GCC (%d.%d.%d) %u-bit",
- msg_hash_to_str(MSG_COMPILER),
- __GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__, (unsigned)
- (CHAR_BIT * sizeof(size_t)));
- #else
- snprintf(s, len, "%s %u-bit",
- msg_hash_to_str(MSG_UNKNOWN_COMPILER),
- (unsigned)(CHAR_BIT * sizeof(size_t)));
- #endif
- break;
- default:
- case RARCH_CAPABILITIES_NONE:
- break;
- }
- return 0;
- }
- void retroarch_set_current_core_type(
- enum rarch_core_type type, bool explicitly_set)
- {
- struct rarch_state *p_rarch = &rarch_st;
- if (p_rarch->has_set_core)
- return;
- if (explicitly_set)
- {
- p_rarch->has_set_core = true;
- p_rarch->explicit_current_core_type = type;
- }
- p_rarch->current_core_type = type;
- }
- /**
- * retroarch_fail:
- * @error_code : Error code.
- * @error : Error message to show.
- *
- * Sanely kills the program.
- **/
- static void retroarch_fail(int error_code, const char *error)
- {
- struct rarch_state *p_rarch = &rarch_st;
- /* We cannot longjmp unless we're in retroarch_main_init().
- * If not, something went very wrong, and we should
- * just exit right away. */
- retro_assert(p_rarch->rarch_error_on_init);
- strlcpy(p_rarch->error_string,
- error, sizeof(p_rarch->error_string));
- longjmp(p_rarch->error_sjlj_context, error_code);
- }
- bool retroarch_main_quit(void)
- {
- struct rarch_state *p_rarch = &rarch_st;
- global_t *global = &p_rarch->g_extern;
- #ifdef HAVE_DISCORD
- discord_state_t *discord_st = &p_rarch->discord_st;
- if (discord_is_inited)
- {
- discord_userdata_t userdata;
- userdata.status = DISCORD_PRESENCE_SHUTDOWN;
- command_event(CMD_EVENT_DISCORD_UPDATE, &userdata);
- }
- if (discord_st->ready)
- {
- Discord_ClearPresence();
- Discord_UpdateConnection();
- Discord_Shutdown();
- discord_st->ready = false;
- }
- discord_is_inited = false;
- #endif
- if (!p_rarch->runloop_shutdown_initiated)
- {
- command_event_save_auto_state(p_rarch->configuration_settings,
- global,
- p_rarch);
- /* If any save states are in progress, wait
- * until all tasks are complete (otherwise
- * save state file may be truncated) */
- content_wait_for_save_state_task();
- #ifdef HAVE_CONFIGFILE
- if (p_rarch->runloop_overrides_active)
- command_event_disable_overrides(p_rarch);
- #endif
- #if defined(HAVE_CG) || defined(HAVE_GLSL) || defined(HAVE_SLANG) || defined(HAVE_HLSL)
- p_rarch->runtime_shader_preset[0] = '\0';
- #endif
- #ifdef HAVE_CONFIGFILE
- if ( p_rarch->runloop_remaps_core_active
- || p_rarch->runloop_remaps_content_dir_active
- || p_rarch->runloop_remaps_game_active
- )
- input_remapping_set_defaults(true);
- #endif
- }
- p_rarch->runloop_shutdown_initiated = true;
- retroarch_menu_running_finished(true);
- return true;
- }
- void runloop_msg_queue_push(const char *msg,
- unsigned prio, unsigned duration,
- bool flush,
- char *title,
- enum message_queue_icon icon,
- enum message_queue_category category)
- {
- struct rarch_state *p_rarch = &rarch_st;
- #if defined(HAVE_GFX_WIDGETS)
- bool widgets_active = p_rarch->widgets_active;
- #endif
- RUNLOOP_MSG_QUEUE_LOCK(p_rarch);
- #ifdef HAVE_ACCESSIBILITY
- if (is_accessibility_enabled(p_rarch))
- accessibility_speak_priority(p_rarch, (char*) msg, 0);
- #endif
- #if defined(HAVE_GFX_WIDGETS)
- if (widgets_active)
- {
- gfx_widgets_msg_queue_push(
- &p_rarch->dispwidget_st,
- NULL,
- msg,
- roundf((float)duration / 60.0f * 1000.0f),
- title,
- icon,
- category,
- prio,
- flush,
- #ifdef HAVE_MENU
- p_rarch->menu_driver_alive
- #else
- false
- #endif
- );
- duration = duration * 60 / 1000;
- }
- else
- #endif
- {
- if (flush)
- msg_queue_clear(&p_rarch->runloop_msg_queue);
- msg_queue_push(&p_rarch->runloop_msg_queue, msg,
- prio, duration,
- title, icon, category);
- p_rarch->runloop_msg_queue_size = msg_queue_size(
- &p_rarch->runloop_msg_queue);
- }
- ui_companion_driver_msg_queue_push(p_rarch,
- msg,
- prio, duration, flush);
- RUNLOOP_MSG_QUEUE_UNLOCK(p_rarch);
- }
- void runloop_get_status(bool *is_paused, bool *is_idle,
- bool *is_slowmotion, bool *is_perfcnt_enable)
- {
- struct rarch_state *p_rarch = &rarch_st;
- *is_paused = p_rarch->runloop_paused;
- *is_idle = p_rarch->runloop_idle;
- *is_slowmotion = p_rarch->runloop_slowmotion;
- *is_perfcnt_enable = p_rarch->runloop_perfcnt_enable;
- }
- #ifdef HAVE_MENU
- static bool input_driver_toggle_button_combo(
- unsigned mode,
- retro_time_t current_time,
- input_bits_t* p_input)
- {
- switch (mode)
- {
- case INPUT_TOGGLE_DOWN_Y_L_R:
- if (BIT256_GET_PTR(p_input, RETRO_DEVICE_ID_JOYPAD_DOWN) &&
- BIT256_GET_PTR(p_input, RETRO_DEVICE_ID_JOYPAD_Y) &&
- BIT256_GET_PTR(p_input, RETRO_DEVICE_ID_JOYPAD_L) &&
- BIT256_GET_PTR(p_input, RETRO_DEVICE_ID_JOYPAD_R))
- return true;
- break;
- case INPUT_TOGGLE_L3_R3:
- if (BIT256_GET_PTR(p_input, RETRO_DEVICE_ID_JOYPAD_L3) &&
- BIT256_GET_PTR(p_input, RETRO_DEVICE_ID_JOYPAD_R3))
- return true;
- break;
- case INPUT_TOGGLE_L1_R1_START_SELECT:
- if (BIT256_GET_PTR(p_input, RETRO_DEVICE_ID_JOYPAD_L) &&
- BIT256_GET_PTR(p_input, RETRO_DEVICE_ID_JOYPAD_R) &&
- BIT256_GET_PTR(p_input, RETRO_DEVICE_ID_JOYPAD_START) &&
- BIT256_GET_PTR(p_input, RETRO_DEVICE_ID_JOYPAD_SELECT))
- return true;
- break;
- case INPUT_TOGGLE_START_SELECT:
- if (BIT256_GET_PTR(p_input, RETRO_DEVICE_ID_JOYPAD_START) &&
- BIT256_GET_PTR(p_input, RETRO_DEVICE_ID_JOYPAD_SELECT))
- return true;
- break;
- case INPUT_TOGGLE_L3_R:
- if (BIT256_GET_PTR(p_input, RETRO_DEVICE_ID_JOYPAD_L3) &&
- BIT256_GET_PTR(p_input, RETRO_DEVICE_ID_JOYPAD_R))
- return true;
- break;
- case INPUT_TOGGLE_L_R:
- if (BIT256_GET_PTR(p_input, RETRO_DEVICE_ID_JOYPAD_L) &&
- BIT256_GET_PTR(p_input, RETRO_DEVICE_ID_JOYPAD_R))
- return true;
- break;
- case INPUT_TOGGLE_DOWN_SELECT:
- if (BIT256_GET_PTR(p_input, RETRO_DEVICE_ID_JOYPAD_DOWN) &&
- BIT256_GET_PTR(p_input, RETRO_DEVICE_ID_JOYPAD_SELECT))
- return true;
- break;
- case INPUT_TOGGLE_L2_R2:
- if (BIT256_GET_PTR(p_input, RETRO_DEVICE_ID_JOYPAD_L2) &&
- BIT256_GET_PTR(p_input, RETRO_DEVICE_ID_JOYPAD_R2))
- return true;
- break;
- case INPUT_TOGGLE_HOLD_START:
- {
- static rarch_timer_t timer = {0};
- if (!BIT256_GET_PTR(p_input, RETRO_DEVICE_ID_JOYPAD_START))
- {
- /* timer only runs while start is held down */
- RARCH_TIMER_END(timer);
- return false;
- }
- /* User started holding down the start button, start the timer */
- if (!timer.timer_begin)
- {
- uint64_t current_usec = cpu_features_get_time_usec();
- RARCH_TIMER_BEGIN_NEW_TIME_USEC(timer,
- current_usec,
- HOLD_BTN_DELAY_SEC * 1000000);
- timer.timer_begin = true;
- timer.timer_end = false;
- }
- RARCH_TIMER_TICK(timer, current_time);
- if (!timer.timer_end && RARCH_TIMER_HAS_EXPIRED(timer))
- {
- /* start has been held down long enough,
- * stop timer and enter menu */
- RARCH_TIMER_END(timer);
- return true;
- }
- return false;
- }
- case INPUT_TOGGLE_HOLD_SELECT:
- {
- static rarch_timer_t timer = {0};
- if (!BIT256_GET_PTR(p_input, RETRO_DEVICE_ID_JOYPAD_SELECT))
- {
- /* timer only runs while select is held down */
- RARCH_TIMER_END(timer);
- return false;
- }
- /* user started holding down the select button, start the timer */
- if (!timer.timer_begin)
- {
- uint64_t current_usec = cpu_features_get_time_usec();
- RARCH_TIMER_BEGIN_NEW_TIME_USEC(timer,
- current_usec,
- HOLD_BTN_DELAY_SEC * 1000000);
- timer.timer_begin = true;
- timer.timer_end = false;
- }
- RARCH_TIMER_TICK(timer, current_time);
- if (!timer.timer_end && RARCH_TIMER_HAS_EXPIRED(timer))
- {
- /* select has been held down long enough,
- * stop timer and enter menu */
- RARCH_TIMER_END(timer);
- return true;
- }
- return false;
- }
- default:
- case INPUT_TOGGLE_NONE:
- break;
- }
- return false;
- }
- /* Display the libretro core's framebuffer onscreen. */
- static bool menu_display_libretro(
- struct rarch_state *p_rarch,
- bool libretro_running,
- retro_time_t current_time)
- {
- bool runloop_idle = p_rarch->runloop_idle;
- if ( p_rarch->video_driver_poke &&
- p_rarch->video_driver_poke->set_texture_enable)
- p_rarch->video_driver_poke->set_texture_enable(
- p_rarch->video_driver_data,
- true, false);
- if (libretro_running)
- {
- if (!p_rarch->input_driver_block_libretro_input)
- p_rarch->input_driver_block_libretro_input = true;
- core_run();
- p_rarch->libretro_core_runtime_usec +=
- rarch_core_runtime_tick(p_rarch, current_time);
- p_rarch->input_driver_block_libretro_input = false;
- return false;
- }
- if (runloop_idle)
- {
- #ifdef HAVE_DISCORD
- discord_userdata_t userdata;
- userdata.status = DISCORD_PRESENCE_GAME_PAUSED;
- command_event(CMD_EVENT_DISCORD_UPDATE, &userdata);
- #endif
- return false;
- }
- return true;
- }
- #endif
- static void update_savestate_slot(int state_slot)
- {
- char msg[128];
- msg[0] = '\0';
- snprintf(msg, sizeof(msg), "%s: %d",
- msg_hash_to_str(MSG_STATE_SLOT),
- state_slot);
- runloop_msg_queue_push(msg, 2, 180, true, NULL,
- MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO);
- RARCH_LOG("%s\n", msg);
- }
- static enum runloop_state runloop_check_state(
- struct rarch_state *p_rarch,
- settings_t *settings,
- retro_time_t current_time)
- {
- input_bits_t current_bits;
- #ifdef HAVE_MENU
- static input_bits_t last_input = {{0}};
- #endif
- static bool old_focus = true;
- struct retro_callbacks *cbs = &p_rarch->retro_ctx;
- bool is_focused = false;
- bool is_alive = false;
- uint64_t frame_count = 0;
- bool focused = true;
- bool rarch_is_initialized = p_rarch->rarch_is_inited;
- bool runloop_paused = p_rarch->runloop_paused;
- float fastforward_ratio = settings->floats.fastforward_ratio;
- bool pause_nonactive = settings->bools.pause_nonactive;
- #ifdef HAVE_MENU
- menu_handle_t *menu = p_rarch->menu_driver_data;
- unsigned menu_toggle_gamepad_combo = settings->uints.input_menu_toggle_gamepad_combo;
- bool menu_driver_binding_state = p_rarch->menu_driver_is_binding;
- bool menu_is_alive = p_rarch->menu_driver_alive;
- bool display_kb = menu_input_dialog_get_display_kb();
- #endif
- #if defined(HAVE_GFX_WIDGETS)
- bool widgets_active = p_rarch->widgets_active;
- #endif
- #ifdef HAVE_CHEEVOS
- bool cheevos_hardcore_active = rcheevos_hardcore_active();
- #endif
- #if defined(HAVE_TRANSLATE) && defined(HAVE_GFX_WIDGETS)
- if (p_rarch->dispwidget_st.ai_service_overlay_state == 3)
- {
- command_event(CMD_EVENT_PAUSE, NULL);
- p_rarch->dispwidget_st.ai_service_overlay_state = 1;
- }
- #endif
- #ifdef HAVE_LIBNX
- /* Should be called once per frame */
- if (!appletMainLoop())
- return RUNLOOP_STATE_QUIT;
- #endif
- #ifdef _3DS
- /* Should be called once per frame */
- if (!aptMainLoop())
- return RUNLOOP_STATE_QUIT;
- #endif
- BIT256_CLEAR_ALL_PTR(¤t_bits);
- p_rarch->input_driver_block_libretro_input = false;
- p_rarch->input_driver_block_hotkey = false;
- if (p_rarch->keyboard_mapping_blocked)
- p_rarch->input_driver_block_hotkey = true;
- {
- rarch_joypad_info_t joypad_info;
- unsigned port = 0;
- int input_hotkey_block_delay = settings->uints.input_hotkey_block_delay;
- input_driver_t *current_input = p_rarch->current_input;
- const struct retro_keybind *binds_norm = &input_config_binds[port][RARCH_ENABLE_HOTKEY];
- const struct retro_keybind *binds_auto = &input_autoconf_binds[port][RARCH_ENABLE_HOTKEY];
- const struct retro_keybind *binds = input_config_binds[port];
- struct retro_keybind *auto_binds = input_autoconf_binds[port];
- struct retro_keybind *general_binds = input_config_binds[port];
- #ifdef HAVE_MENU
- bool menu_input_active = menu_is_alive && !(settings->bools.menu_unified_controls && !display_kb);
- #else
- bool menu_input_active = false;
- #endif
- #ifdef HAVE_MFI
- const input_device_driver_t
- *sec_joypad = p_rarch->sec_joypad;
- #else
- const input_device_driver_t
- *sec_joypad = NULL;
- #endif
- joypad_info.joy_idx = settings->uints.input_joypad_map[port];
- joypad_info.auto_binds = input_autoconf_binds[joypad_info.joy_idx];
- joypad_info.axis_threshold = p_rarch->input_driver_axis_threshold;
- #ifdef HAVE_MENU
- if (menu_input_active)
- {
- unsigned k;
- unsigned x_plus = RARCH_ANALOG_LEFT_X_PLUS;
- unsigned y_plus = RARCH_ANALOG_LEFT_Y_PLUS;
- unsigned x_minus = RARCH_ANALOG_LEFT_X_MINUS;
- unsigned y_minus = RARCH_ANALOG_LEFT_Y_MINUS;
- /* Push analog to D-Pad mappings to binds. */
- for (k = RETRO_DEVICE_ID_JOYPAD_UP; k <= RETRO_DEVICE_ID_JOYPAD_RIGHT; k++)
- {
- (auto_binds)[k].orig_joyaxis = (auto_binds)[k].joyaxis;
- (general_binds)[k].orig_joyaxis = (general_binds)[k].joyaxis;
- }
- if (!INHERIT_JOYAXIS(auto_binds))
- {
- unsigned j = x_plus + 3;
- /* Inherit joyaxis from analogs. */
- for (k = RETRO_DEVICE_ID_JOYPAD_UP; k <= RETRO_DEVICE_ID_JOYPAD_RIGHT; k++)
- (auto_binds)[k].joyaxis = (auto_binds)[j--].joyaxis;
- }
- if (!INHERIT_JOYAXIS(general_binds))
- {
- unsigned j = x_plus + 3;
- /* Inherit joyaxis from analogs. */
- for (k = RETRO_DEVICE_ID_JOYPAD_UP; k <= RETRO_DEVICE_ID_JOYPAD_RIGHT; k++)
- (general_binds)[k].joyaxis = (general_binds)[j--].joyaxis;
- }
- }
- #endif
- input_keys_pressed(port, menu_input_active, input_hotkey_block_delay, p_rarch,
- ¤t_bits, &binds, binds_norm, binds_auto,
- &joypad_info);
- #ifdef HAVE_MENU
- if (menu_input_active)
- {
- unsigned j;
- /* Restores analog D-pad binds temporarily overridden. */
- for (j = RETRO_DEVICE_ID_JOYPAD_UP; j <= RETRO_DEVICE_ID_JOYPAD_RIGHT; j++)
- {
- (auto_binds)[j].joyaxis = (auto_binds)[j].orig_joyaxis;
- (general_binds)[j].joyaxis = (general_binds)[j].orig_joyaxis;
- }
- if ( !display_kb
- && current_input->input_state)
- {
- unsigned i;
- unsigned ids[][2] =
- {
- {RETROK_SPACE, RETRO_DEVICE_ID_JOYPAD_START },
- {RETROK_SLASH, RETRO_DEVICE_ID_JOYPAD_X },
- {RETROK_RSHIFT, RETRO_DEVICE_ID_JOYPAD_SELECT },
- {RETROK_RIGHT, RETRO_DEVICE_ID_JOYPAD_RIGHT },
- {RETROK_LEFT, RETRO_DEVICE_ID_JOYPAD_LEFT },
- {RETROK_DOWN, RETRO_DEVICE_ID_JOYPAD_DOWN },
- {RETROK_UP, RETRO_DEVICE_ID_JOYPAD_UP },
- {RETROK_PAGEUP, RETRO_DEVICE_ID_JOYPAD_L },
- {RETROK_PAGEDOWN, RETRO_DEVICE_ID_JOYPAD_R },
- {0, RARCH_QUIT_KEY },
- {0, RARCH_FULLSCREEN_TOGGLE_KEY },
- {RETROK_BACKSPACE, RETRO_DEVICE_ID_JOYPAD_B },
- {RETROK_RETURN, RETRO_DEVICE_ID_JOYPAD_A },
- {RETROK_DELETE, RETRO_DEVICE_ID_JOYPAD_Y },
- {0, RARCH_UI_COMPANION_TOGGLE },
- {0, RARCH_FPS_TOGGLE },
- {0, RARCH_SEND_DEBUG_INFO },
- {0, RARCH_NETPLAY_HOST_TOGGLE },
- {0, RARCH_MENU_TOGGLE },
- };
- ids[9][0] = input_config_binds[port][RARCH_QUIT_KEY].key;
- ids[10][0] = input_config_binds[port][RARCH_FULLSCREEN_TOGGLE_KEY].key;
- ids[14][0] = input_config_binds[port][RARCH_UI_COMPANION_TOGGLE].key;
- ids[15][0] = input_config_binds[port][RARCH_FPS_TOGGLE].key;
- ids[16][0] = input_config_binds[port][RARCH_SEND_DEBUG_INFO].key;
- ids[17][0] = input_config_binds[port][RARCH_NETPLAY_HOST_TOGGLE].key;
- ids[18][0] = input_config_binds[port][RARCH_MENU_TOGGLE].key;
- if (settings->bools.input_menu_swap_ok_cancel_buttons)
- {
- ids[11][1] = RETRO_DEVICE_ID_JOYPAD_A;
- ids[12][1] = RETRO_DEVICE_ID_JOYPAD_B;
- }
- for (i = 0; i < ARRAY_SIZE(ids); i++)
- {
- if (current_input->input_state(
- p_rarch->current_input_data,
- p_rarch->joypad,
- sec_joypad,
- &joypad_info, &binds,
- p_rarch->keyboard_mapping_blocked,
- port,
- RETRO_DEVICE_KEYBOARD, 0, ids[i][0]))
- BIT256_SET_PTR(¤t_bits, ids[i][1]);
- }
- }
- }
- else
- #endif
- {
- #ifdef HAVE_ACCESSIBILITY
- #ifdef HAVE_TRANSLATE
- if (settings->bools.ai_service_enable)
- {
- unsigned i;
- p_rarch->gamepad_input_override = 0;
- for (i = 0; i < MAX_USERS; i++)
- {
- /* Set gamepad input override */
- if (p_rarch->ai_gamepad_state[i] == 2)
- p_rarch->gamepad_input_override |= (1 << i);
- p_rarch->ai_gamepad_state[i] = 0;
- }
- }
- #endif
- #endif
- }
- }
- #ifdef HAVE_MENU
- last_input = current_bits;
- if (
- ((menu_toggle_gamepad_combo != INPUT_TOGGLE_NONE) &&
- input_driver_toggle_button_combo(
- menu_toggle_gamepad_combo, current_time,
- &last_input)))
- BIT256_SET(current_bits, RARCH_MENU_TOGGLE);
- #endif
- if (p_rarch->input_driver_flushing_input > 0)
- {
- bool input_active = bits_any_set(current_bits.data, ARRAY_SIZE(current_bits.data));
- p_rarch->input_driver_flushing_input = input_active
- ? p_rarch->input_driver_flushing_input
- : (p_rarch->input_driver_flushing_input - 1);
- if (input_active || (p_rarch->input_driver_flushing_input > 0))
- {
- BIT256_CLEAR_ALL(current_bits);
- if (runloop_paused)
- BIT256_SET(current_bits, RARCH_PAUSE_TOGGLE);
- }
- }
- if (!VIDEO_DRIVER_IS_THREADED_INTERNAL())
- {
- const ui_application_t *application = p_rarch->ui_companion
- ? p_rarch->ui_companion->application
- : NULL;
- if (application)
- application->process_events();
- }
- frame_count = p_rarch->video_driver_frame_count;
- is_alive = p_rarch->current_video
- ? p_rarch->current_video->alive(p_rarch->video_driver_data)
- : true;
- is_focused = VIDEO_HAS_FOCUS(p_rarch);
- #ifdef HAVE_MENU
- if (menu_driver_binding_state)
- BIT256_CLEAR_ALL(current_bits);
- #endif
- /* Check fullscreen toggle */
- {
- bool fullscreen_toggled = !runloop_paused
- #ifdef HAVE_MENU
- || menu_is_alive;
- #else
- ;
- #endif
- HOTKEY_CHECK(RARCH_FULLSCREEN_TOGGLE_KEY, CMD_EVENT_FULLSCREEN_TOGGLE,
- fullscreen_toggled, NULL);
- }
- /* Check mouse grab toggle */
- HOTKEY_CHECK(RARCH_GRAB_MOUSE_TOGGLE, CMD_EVENT_GRAB_MOUSE_TOGGLE, true, NULL);
- #ifdef HAVE_OVERLAY
- if (settings->bools.input_overlay_enable)
- {
- static char prev_overlay_restore = false;
- static unsigned last_width = 0;
- static unsigned last_height = 0;
- unsigned video_driver_width = p_rarch->video_driver_width;
- unsigned video_driver_height = p_rarch->video_driver_height;
- bool check_next_rotation = true;
- bool input_overlay_hide_in_menu = settings->bools.input_overlay_hide_in_menu;
- bool input_overlay_hide_when_gamepad_connected = settings->bools.input_overlay_hide_when_gamepad_connected;
- bool input_overlay_auto_rotate = settings->bools.input_overlay_auto_rotate;
- /* Check whether overlay should be hidden
- * when a gamepad is connected */
- if (input_overlay_hide_when_gamepad_connected)
- {
- static bool last_controller_connected = false;
- bool controller_connected = (input_config_get_device_name(0) != NULL);
- if (controller_connected != last_controller_connected)
- {
- if (controller_connected)
- retroarch_overlay_deinit(p_rarch);
- else
- retroarch_overlay_init(p_rarch);
- last_controller_connected = controller_connected;
- }
- }
- /* Check next overlay */
- HOTKEY_CHECK(RARCH_OVERLAY_NEXT, CMD_EVENT_OVERLAY_NEXT, true, &check_next_rotation);
- /* Ensure overlay is restored after displaying osk */
- if (p_rarch->input_driver_keyboard_linefeed_enable)
- prev_overlay_restore = true;
- else if (prev_overlay_restore)
- {
- if (!input_overlay_hide_in_menu)
- retroarch_overlay_init(p_rarch);
- prev_overlay_restore = false;
- }
- /* Check whether video aspect has changed */
- if ((video_driver_width != last_width) ||
- (video_driver_height != last_height))
- {
- /* Update scaling/offset factors */
- command_event(CMD_EVENT_OVERLAY_SET_SCALE_FACTOR, NULL);
- /* Check overlay rotation, if required */
- if (input_overlay_auto_rotate)
- input_overlay_auto_rotate_(p_rarch,
- p_rarch->overlay_ptr);
- last_width = video_driver_width;
- last_height = video_driver_height;
- }
- }
- #endif
- /* Check quit key */
- {
- bool trig_quit_key, quit_press_twice;
- static bool quit_key = false;
- static bool old_quit_key = false;
- static bool runloop_exec = false;
- quit_key = BIT256_GET(
- current_bits, RARCH_QUIT_KEY);
- trig_quit_key = quit_key && !old_quit_key;
- old_quit_key = quit_key;
- quit_press_twice = settings->bools.quit_press_twice;
- /* Check double press if enabled */
- if (trig_quit_key && quit_press_twice)
- {
- static retro_time_t quit_key_time = 0;
- retro_time_t cur_time = current_time;
- trig_quit_key = (cur_time - quit_key_time < QUIT_DELAY_USEC);
- quit_key_time = cur_time;
- if (!trig_quit_key)
- {
- float target_hz = 0.0;
- rarch_environment_cb(
- RETRO_ENVIRONMENT_GET_TARGET_REFRESH_RATE, &target_hz);
- runloop_msg_queue_push(msg_hash_to_str(MSG_PRESS_AGAIN_TO_QUIT), 1,
- QUIT_DELAY_USEC * target_hz / 1000000,
- true, NULL,
- MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO);
- }
- }
- if (TIME_TO_EXIT(trig_quit_key))
- {
- bool quit_runloop = false;
- #ifdef HAVE_SCREENSHOTS
- unsigned runloop_max_frames = p_rarch->runloop_max_frames;
- if ((runloop_max_frames != 0)
- && (frame_count >= runloop_max_frames)
- && p_rarch->runloop_max_frames_screenshot)
- {
- const char *screenshot_path = NULL;
- bool fullpath = false;
- if (string_is_empty(p_rarch->runloop_max_frames_screenshot_path))
- screenshot_path = path_get(RARCH_PATH_BASENAME);
- else
- {
- fullpath = true;
- screenshot_path = p_rarch->runloop_max_frames_screenshot_path;
- }
- RARCH_LOG("Taking a screenshot before exiting...\n");
- /* Take a screenshot before we exit. */
- if (!take_screenshot(settings->paths.directory_screenshot,
- screenshot_path, false,
- video_driver_cached_frame_has_valid_framebuffer(), fullpath, false))
- {
- RARCH_ERR("Could not take a screenshot before exiting.\n");
- }
- }
- #endif
- if (runloop_exec)
- runloop_exec = false;
- if (p_rarch->runloop_core_shutdown_initiated &&
- settings->bools.load_dummy_on_core_shutdown)
- {
- content_ctx_info_t content_info;
- content_info.argc = 0;
- content_info.argv = NULL;
- content_info.args = NULL;
- content_info.environ_get = NULL;
- if (task_push_start_dummy_core(&content_info))
- {
- /* Loads dummy core instead of exiting RetroArch completely.
- * Aborts core shutdown if invoked. */
- p_rarch->runloop_shutdown_initiated = false;
- p_rarch->runloop_core_shutdown_initiated = false;
- }
- else
- quit_runloop = true;
- }
- else
- quit_runloop = true;
- p_rarch->runloop_core_running = false;
- if (quit_runloop)
- {
- old_quit_key = quit_key;
- retroarch_main_quit();
- return RUNLOOP_STATE_QUIT;
- }
- }
- }
- #if defined(HAVE_MENU) || defined(HAVE_GFX_WIDGETS)
- gfx_animation_update(
- &p_rarch->anim,
- current_time,
- settings->bools.menu_timedate_enable,
- settings->floats.menu_ticker_speed,
- p_rarch->video_driver_width,
- p_rarch->video_driver_height);
- #if defined(HAVE_GFX_WIDGETS)
- if (widgets_active)
- {
- bool rarch_force_fullscreen = p_rarch->rarch_force_fullscreen;
- bool video_is_fullscreen = settings->bools.video_fullscreen ||
- rarch_force_fullscreen;
- RUNLOOP_MSG_QUEUE_LOCK(p_rarch);
- gfx_widgets_iterate(
- &p_rarch->dispwidget_st,
- &p_rarch->dispgfx,
- p_rarch->video_driver_width,
- p_rarch->video_driver_height,
- video_is_fullscreen,
- settings->paths.directory_assets,
- settings->paths.path_font,
- VIDEO_DRIVER_IS_THREADED_INTERNAL());
- RUNLOOP_MSG_QUEUE_UNLOCK(p_rarch);
- }
- #endif
- #ifdef HAVE_MENU
- if (menu_is_alive)
- {
- enum menu_action action;
- static input_bits_t old_input = {{0}};
- static enum menu_action
- old_action = MENU_ACTION_CANCEL;
- struct menu_state *menu_st = &p_rarch->menu_driver_state;
- bool focused = false;
- input_bits_t trigger_input = current_bits;
- global_t *global = &p_rarch->g_extern;
- cbs->poll_cb();
- bits_clear_bits(trigger_input.data, old_input.data,
- ARRAY_SIZE(trigger_input.data));
- action = (enum menu_action)menu_event(
- p_rarch,
- ¤t_bits, &trigger_input, display_kb);
- focused = pause_nonactive ? is_focused : true;
- focused = focused &&
- !p_rarch->main_ui_companion_is_on_foreground;
- if (global)
- {
- if (action == old_action)
- {
- retro_time_t press_time = current_time;
- if (action == MENU_ACTION_NOOP)
- global->menu.noop_press_time = press_time - global->menu.noop_start_time;
- else
- global->menu.action_press_time = press_time - global->menu.action_start_time;
- }
- else
- {
- if (action == MENU_ACTION_NOOP)
- {
- global->menu.noop_start_time = current_time;
- global->menu.noop_press_time = 0;
- if (global->menu_prev_action == old_action)
- global->menu.action_start_time = global->menu.prev_start_time;
- else
- global->menu.action_start_time = current_time;
- }
- else
- {
- if ( global->menu_prev_action == action &&
- global->menu.noop_press_time < 200000) /* 250ms */
- {
- global->menu.action_start_time = global->menu.prev_start_time;
- global->menu.action_press_time = current_time - global->menu.action_start_time;
- }
- else
- {
- global->menu.prev_start_time = current_time;
- global->menu_prev_action = action;
- global->menu.action_press_time = 0;
- }
- }
- }
- }
- /* Get current time */
- menu_st->current_time_us = current_time;
- /* Iterate the menu driver for one frame. */
- if (menu_st->pending_quick_menu)
- {
- /* If the user had requested that the Quick Menu
- * be spawned during the previous frame, do this now
- * and exit the function to go to the next frame.
- */
- menu_entries_flush_stack(NULL, MENU_SETTINGS);
- gfx_display_set_msg_force(true);
- generic_action_ok_displaylist_push("", NULL,
- "", 0, 0, 0, ACTION_OK_DL_CONTENT_SETTINGS);
- menu_st->selection_ptr = 0;
- menu_st->pending_quick_menu = false;
- }
- else if (!menu_driver_iterate(p_rarch, action, current_time))
- {
- if (p_rarch->rarch_error_on_init)
- {
- content_ctx_info_t content_info = {0};
- task_push_start_dummy_core(&content_info);
- }
- else
- retroarch_menu_running_finished(false);
- }
- if (focused || !p_rarch->runloop_idle)
- {
- bool rarch_is_inited = p_rarch->rarch_is_inited;
- bool menu_pause_libretro = settings->bools.menu_pause_libretro;
- bool libretro_running = !menu_pause_libretro
- && rarch_is_inited
- && (p_rarch->current_core_type != CORE_TYPE_DUMMY);
- if (menu)
- {
- if (BIT64_GET(menu->state, MENU_STATE_RENDER_FRAMEBUFFER)
- != BIT64_GET(menu->state, MENU_STATE_RENDER_MESSAGEBOX))
- BIT64_SET(menu->state, MENU_STATE_RENDER_FRAMEBUFFER);
- if (BIT64_GET(menu->state, MENU_STATE_RENDER_FRAMEBUFFER))
- p_rarch->dispgfx.framebuf_dirty = true;
- if (BIT64_GET(menu->state, MENU_STATE_RENDER_MESSAGEBOX)
- && !string_is_empty(menu->menu_state_msg))
- {
- if (menu->driver_ctx->render_messagebox)
- menu->driver_ctx->render_messagebox(
- menu->userdata,
- menu->menu_state_msg);
- if (p_rarch->main_ui_companion_is_on_foreground)
- {
- if ( p_rarch->ui_companion &&
- p_rarch->ui_companion->render_messagebox)
- p_rarch->ui_companion->render_messagebox(menu->menu_state_msg);
- }
- }
- if (BIT64_GET(menu->state, MENU_STATE_BLIT))
- {
- if (menu->driver_ctx->render)
- menu->driver_ctx->render(
- menu->userdata,
- p_rarch->video_driver_width,
- p_rarch->video_driver_height,
- p_rarch->runloop_idle);
- }
- if (p_rarch->menu_driver_alive && !p_rarch->runloop_idle)
- if (menu_display_libretro(p_rarch,
- libretro_running, current_time))
- video_driver_cached_frame();
- if (menu->driver_ctx->set_texture)
- menu->driver_ctx->set_texture(menu->userdata);
- menu->state = 0;
- }
- if (settings->bools.audio_enable_menu &&
- !libretro_running)
- audio_driver_menu_sample();
- }
- old_input = current_bits;
- old_action = action;
- if (!focused || p_rarch->runloop_idle)
- return RUNLOOP_STATE_POLLED_AND_SLEEP;
- }
- else
- #endif
- #endif
- {
- if (p_rarch->runloop_idle)
- {
- cbs->poll_cb();
- return RUNLOOP_STATE_POLLED_AND_SLEEP;
- }
- }
- /* Check game focus toggle */
- {
- enum input_game_focus_cmd_type game_focus_cmd = GAME_FOCUS_CMD_TOGGLE;
- HOTKEY_CHECK(RARCH_GAME_FOCUS_TOGGLE, CMD_EVENT_GAME_FOCUS_TOGGLE, true, &game_focus_cmd);
- }
- /* Check if we have pressed the UI companion toggle button */
- HOTKEY_CHECK(RARCH_UI_COMPANION_TOGGLE, CMD_EVENT_UI_COMPANION_TOGGLE, true, NULL);
- /* Check close content key */
- HOTKEY_CHECK(RARCH_CLOSE_CONTENT_KEY, CMD_EVENT_CLOSE_CONTENT, true, NULL);
- #ifdef HAVE_MENU
- /* Check if we have pressed the menu toggle button */
- {
- static bool old_pressed = false;
- char *menu_driver = settings->arrays.menu_driver;
- bool pressed = BIT256_GET(
- current_bits, RARCH_MENU_TOGGLE) &&
- !string_is_equal(menu_driver, "null");
- bool core_type_is_dummy = p_rarch->current_core_type == CORE_TYPE_DUMMY;
- if (p_rarch->menu_keyboard_key_state[RETROK_F1] == 1)
- {
- if (p_rarch->menu_driver_alive)
- {
- if (rarch_is_initialized && !core_type_is_dummy)
- {
- retroarch_menu_running_finished(false);
- p_rarch->menu_keyboard_key_state[RETROK_F1] =
- ((p_rarch->menu_keyboard_key_state[RETROK_F1] & 1) << 1) | false;
- }
- }
- }
- else if ((!p_rarch->menu_keyboard_key_state[RETROK_F1] &&
- (pressed && !old_pressed)) ||
- core_type_is_dummy)
- {
- if (p_rarch->menu_driver_alive)
- {
- if (rarch_is_initialized && !core_type_is_dummy)
- retroarch_menu_running_finished(false);
- }
- else
- {
- retroarch_menu_running();
- }
- }
- else
- p_rarch->menu_keyboard_key_state[RETROK_F1] =
- ((p_rarch->menu_keyboard_key_state[RETROK_F1] & 1) << 1) | false;
- old_pressed = pressed;
- }
- /* Check if we have pressed the FPS toggle button */
- HOTKEY_CHECK(RARCH_FPS_TOGGLE, CMD_EVENT_FPS_TOGGLE, true, NULL);
- /* Check if we have pressed the netplay host toggle button */
- HOTKEY_CHECK(RARCH_NETPLAY_HOST_TOGGLE, CMD_EVENT_NETPLAY_HOST_TOGGLE, true, NULL);
- if (p_rarch->menu_driver_alive)
- {
- if (!settings->bools.menu_throttle_framerate && !fastforward_ratio)
- return RUNLOOP_STATE_MENU_ITERATE;
- return RUNLOOP_STATE_END;
- }
- #endif
- if (pause_nonactive)
- focused = is_focused;
- #ifdef HAVE_SCREENSHOTS
- /* Check if we have pressed the screenshot toggle button */
- HOTKEY_CHECK(RARCH_SCREENSHOT, CMD_EVENT_TAKE_SCREENSHOT, true, NULL);
- #endif
- /* Check if we have pressed the audio mute toggle button */
- HOTKEY_CHECK(RARCH_MUTE, CMD_EVENT_AUDIO_MUTE_TOGGLE, true, NULL);
- /* Check if we have pressed the OSK toggle button */
- HOTKEY_CHECK(RARCH_OSK, CMD_EVENT_OSK_TOGGLE, true, NULL);
- /* Check if we have pressed the recording toggle button */
- HOTKEY_CHECK(RARCH_RECORDING_TOGGLE, CMD_EVENT_RECORDING_TOGGLE, true, NULL);
- /* Check if we have pressed the streaming toggle button */
- HOTKEY_CHECK(RARCH_STREAMING_TOGGLE, CMD_EVENT_STREAMING_TOGGLE, true, NULL);
- /* Check if we have pressed the Run-Ahead toggle button */
- HOTKEY_CHECK(RARCH_RUNAHEAD_TOGGLE, CMD_EVENT_RUNAHEAD_TOGGLE, true, NULL);
- /* Check if we have pressed the AI Service toggle button */
- HOTKEY_CHECK(RARCH_AI_SERVICE, CMD_EVENT_AI_SERVICE_TOGGLE, true, NULL);
- if (BIT256_GET(current_bits, RARCH_VOLUME_UP))
- command_event(CMD_EVENT_VOLUME_UP, NULL);
- else if (BIT256_GET(current_bits, RARCH_VOLUME_DOWN))
- command_event(CMD_EVENT_VOLUME_DOWN, NULL);
- #ifdef HAVE_NETWORKING
- /* Check Netplay */
- HOTKEY_CHECK(RARCH_NETPLAY_GAME_WATCH, CMD_EVENT_NETPLAY_GAME_WATCH, true, NULL);
- #endif
- /* Check if we have pressed the pause button */
- {
- static bool old_frameadvance = false;
- static bool old_pause_pressed = false;
- bool frameadvance_pressed = false;
- bool trig_frameadvance = false;
- bool pause_pressed = BIT256_GET(current_bits, RARCH_PAUSE_TOGGLE);
- #ifdef HAVE_CHEEVOS
- if (cheevos_hardcore_active)
- {
- static int unpaused_frames = 0;
- /* Frame advance is not allowed when achievement hardcore is active */
- if (!p_rarch->runloop_paused)
- {
- /* Limit pause to approximately three times per second (depending on core framerate) */
- if (unpaused_frames < 20)
- {
- ++unpaused_frames;
- pause_pressed = false;
- }
- }
- else
- unpaused_frames = 0;
- }
- else
- #endif
- {
- frameadvance_pressed = BIT256_GET(current_bits, RARCH_FRAMEADVANCE);
- trig_frameadvance = frameadvance_pressed && !old_frameadvance;
- /* FRAMEADVANCE will set us into pause mode. */
- pause_pressed |= !p_rarch->runloop_paused
- && trig_frameadvance;
- }
- /* Check if libretro pause key was pressed. If so, pause or
- * unpause the libretro core. */
- if (focused && pause_pressed && !old_pause_pressed)
- command_event(CMD_EVENT_PAUSE_TOGGLE, NULL);
- else if (focused && !old_focus)
- command_event(CMD_EVENT_UNPAUSE, NULL);
- else if (!focused && old_focus)
- command_event(CMD_EVENT_PAUSE, NULL);
- old_focus = focused;
- old_pause_pressed = pause_pressed;
- old_frameadvance = frameadvance_pressed;
- if (p_rarch->runloop_paused)
- {
- bool toggle = !p_rarch->runloop_idle ? true : false;
- HOTKEY_CHECK(RARCH_FULLSCREEN_TOGGLE_KEY,
- CMD_EVENT_FULLSCREEN_TOGGLE, true, &toggle);
- /* Check if it's not oneshot */
- #ifdef HAVE_REWIND
- if (!(trig_frameadvance || BIT256_GET(current_bits, RARCH_REWIND)))
- #else
- if (!trig_frameadvance)
- #endif
- focused = false;
- }
- }
- #ifdef HAVE_ACCESSIBILITY
- #ifdef HAVE_TRANSLATE
- /* Copy over the retropad state to a buffer for the translate service
- to send off if it's run. */
- if (settings->bools.ai_service_enable)
- {
- p_rarch->ai_gamepad_state[0] = BIT256_GET(current_bits, RETRO_DEVICE_ID_JOYPAD_B);
- p_rarch->ai_gamepad_state[1] = BIT256_GET(current_bits, RETRO_DEVICE_ID_JOYPAD_Y);
- p_rarch->ai_gamepad_state[2] = BIT256_GET(current_bits, RETRO_DEVICE_ID_JOYPAD_SELECT);
- p_rarch->ai_gamepad_state[3] = BIT256_GET(current_bits, RETRO_DEVICE_ID_JOYPAD_START);
- p_rarch->ai_gamepad_state[4] = BIT256_GET(current_bits, RETRO_DEVICE_ID_JOYPAD_UP);
- p_rarch->ai_gamepad_state[5] = BIT256_GET(current_bits, RETRO_DEVICE_ID_JOYPAD_DOWN);
- p_rarch->ai_gamepad_state[6] = BIT256_GET(current_bits, RETRO_DEVICE_ID_JOYPAD_LEFT);
- p_rarch->ai_gamepad_state[7] = BIT256_GET(current_bits, RETRO_DEVICE_ID_JOYPAD_RIGHT);
- p_rarch->ai_gamepad_state[8] = BIT256_GET(current_bits, RETRO_DEVICE_ID_JOYPAD_A);
- p_rarch->ai_gamepad_state[9] = BIT256_GET(current_bits, RETRO_DEVICE_ID_JOYPAD_X);
- p_rarch->ai_gamepad_state[10] = BIT256_GET(current_bits, RETRO_DEVICE_ID_JOYPAD_L);
- p_rarch->ai_gamepad_state[11] = BIT256_GET(current_bits, RETRO_DEVICE_ID_JOYPAD_R);
- p_rarch->ai_gamepad_state[12] = BIT256_GET(current_bits, RETRO_DEVICE_ID_JOYPAD_L2);
- p_rarch->ai_gamepad_state[13] = BIT256_GET(current_bits, RETRO_DEVICE_ID_JOYPAD_R2);
- p_rarch->ai_gamepad_state[14] = BIT256_GET(current_bits, RETRO_DEVICE_ID_JOYPAD_L3);
- p_rarch->ai_gamepad_state[15] = BIT256_GET(current_bits, RETRO_DEVICE_ID_JOYPAD_R3);
- }
- #endif
- #endif
- if (!focused)
- {
- cbs->poll_cb();
- return RUNLOOP_STATE_POLLED_AND_SLEEP;
- }
- /* Check if we have pressed the fast forward button */
- /* To avoid continuous switching if we hold the button down, we require
- * that the button must go from pressed to unpressed back to pressed
- * to be able to toggle between then.
- */
- {
- static bool old_button_state = false;
- static bool old_hold_button_state = false;
- bool new_button_state = BIT256_GET(
- current_bits, RARCH_FAST_FORWARD_KEY);
- bool new_hold_button_state = BIT256_GET(
- current_bits, RARCH_FAST_FORWARD_HOLD_KEY);
- bool check2 = new_button_state && !old_button_state;
- if (!check2)
- check2 = old_hold_button_state != new_hold_button_state;
- if (check2)
- {
- bool check1 = p_rarch->input_driver_nonblock_state;
- p_rarch->input_driver_nonblock_state = !check1;
- p_rarch->runloop_fastmotion = !check1;
- if (check1)
- p_rarch->fastforward_after_frames = 1;
- driver_set_nonblock_state();
- /* Reset frame time counter when toggling
- * fast-forward off, if required */
- if (!p_rarch->runloop_fastmotion &&
- settings->bools.frame_time_counter_reset_after_fastforwarding)
- p_rarch->video_driver_frame_time_count = 0;
- }
- old_button_state = new_button_state;
- old_hold_button_state = new_hold_button_state;
- /* Display fast-forward notification
- * > Use widgets, if enabled */
- #if defined(HAVE_GFX_WIDGETS)
- if (widgets_active)
- p_rarch->gfx_widgets_fast_forward =
- settings->bools.notification_show_fast_forward ?
- p_rarch->runloop_fastmotion : false;
- else
- #endif
- {
- /* > If widgets are disabled, display fast-forward
- * status via OSD text for 1 frame every frame */
- if (p_rarch->runloop_fastmotion &&
- settings->bools.notification_show_fast_forward)
- runloop_msg_queue_push(
- msg_hash_to_str(MSG_FAST_FORWARD), 1, 1, false, NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO);
- }
- }
- /* Check if we have pressed any of the state slot buttons */
- {
- static bool old_should_slot_increase = false;
- static bool old_should_slot_decrease = false;
- bool should_slot_increase = BIT256_GET(
- current_bits, RARCH_STATE_SLOT_PLUS);
- bool should_slot_decrease = BIT256_GET(
- current_bits, RARCH_STATE_SLOT_MINUS);
- bool check1 = true;
- bool check2 = should_slot_increase && !old_should_slot_increase;
- int addition = 1;
- int state_slot = settings->ints.state_slot;
- if (!check2)
- {
- check2 = should_slot_decrease && !old_should_slot_decrease;
- check1 = state_slot > 0;
- addition = -1;
- }
- /* Checks if the state increase/decrease keys have been pressed
- * for this frame. */
- if (check2)
- {
- int cur_state_slot = state_slot;
- if (check1)
- configuration_set_int(settings, settings->ints.state_slot,
- cur_state_slot + addition);
- update_savestate_slot(settings->ints.state_slot);
- }
- old_should_slot_increase = should_slot_increase;
- old_should_slot_decrease = should_slot_decrease;
- }
- /* Check if we have pressed any of the savestate buttons */
- HOTKEY_CHECK(RARCH_SAVE_STATE_KEY, CMD_EVENT_SAVE_STATE, true, NULL);
- HOTKEY_CHECK(RARCH_LOAD_STATE_KEY, CMD_EVENT_LOAD_STATE, true, NULL);
- #ifdef HAVE_CHEEVOS
- if (!cheevos_hardcore_active)
- #endif
- {
- /* Check if rewind toggle was being held. */
- {
- #ifdef HAVE_REWIND
- char s[128];
- bool rewinding = false;
- unsigned t = 0;
- s[0] = '\0';
- rewinding = state_manager_check_rewind(
- &p_rarch->rewind_st,
- BIT256_GET(current_bits, RARCH_REWIND),
- settings->uints.rewind_granularity,
- p_rarch->runloop_paused,
- s, sizeof(s), &t);
- #if defined(HAVE_GFX_WIDGETS)
- if (widgets_active)
- p_rarch->gfx_widgets_rewinding = rewinding;
- else
- #endif
- {
- if (rewinding)
- runloop_msg_queue_push(s, 0, t, true, NULL,
- MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO);
- }
- #endif
- }
- {
- /* Checks if slowmotion toggle/hold was being pressed and/or held. */
- static bool old_slowmotion_button_state = false;
- static bool old_slowmotion_hold_button_state = false;
- bool new_slowmotion_button_state = BIT256_GET(
- current_bits, RARCH_SLOWMOTION_KEY);
- bool new_slowmotion_hold_button_state = BIT256_GET(
- current_bits, RARCH_SLOWMOTION_HOLD_KEY);
- if (new_slowmotion_button_state && !old_slowmotion_button_state)
- p_rarch->runloop_slowmotion = !p_rarch->runloop_slowmotion;
- else if (old_slowmotion_hold_button_state != new_slowmotion_hold_button_state)
- p_rarch->runloop_slowmotion = new_slowmotion_hold_button_state;
- if (p_rarch->runloop_slowmotion)
- {
- if (settings->uints.video_black_frame_insertion)
- if (!p_rarch->runloop_idle)
- video_driver_cached_frame();
- #if defined(HAVE_GFX_WIDGETS)
- if (!widgets_active)
- #endif
- {
- #ifdef HAVE_REWIND
- struct state_manager_rewind_state
- *rewind_st = &p_rarch->rewind_st;
- if (rewind_st->frame_is_reversed)
- runloop_msg_queue_push(
- msg_hash_to_str(MSG_SLOW_MOTION_REWIND), 1, 1, false, NULL,
- MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO);
- else
- #endif
- runloop_msg_queue_push(
- msg_hash_to_str(MSG_SLOW_MOTION), 1, 1, false,
- NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO);
- }
- }
- old_slowmotion_button_state = new_slowmotion_button_state;
- old_slowmotion_hold_button_state = new_slowmotion_hold_button_state;
- }
- }
- /* Check movie record toggle */
- HOTKEY_CHECK(RARCH_BSV_RECORD_TOGGLE, CMD_EVENT_BSV_RECORDING_TOGGLE, true, NULL);
- /* Check shader prev/next */
- HOTKEY_CHECK(RARCH_SHADER_NEXT, CMD_EVENT_SHADER_NEXT, true, NULL);
- HOTKEY_CHECK(RARCH_SHADER_PREV, CMD_EVENT_SHADER_PREV, true, NULL);
- /* Check if we have pressed any of the disk buttons */
- HOTKEY_CHECK3(
- RARCH_DISK_EJECT_TOGGLE, CMD_EVENT_DISK_EJECT_TOGGLE,
- RARCH_DISK_NEXT, CMD_EVENT_DISK_NEXT,
- RARCH_DISK_PREV, CMD_EVENT_DISK_PREV);
- /* Check if we have pressed the reset button */
- HOTKEY_CHECK(RARCH_RESET, CMD_EVENT_RESET, true, NULL);
- /* Check cheats */
- HOTKEY_CHECK3(
- RARCH_CHEAT_INDEX_PLUS, CMD_EVENT_CHEAT_INDEX_PLUS,
- RARCH_CHEAT_INDEX_MINUS, CMD_EVENT_CHEAT_INDEX_MINUS,
- RARCH_CHEAT_TOGGLE, CMD_EVENT_CHEAT_TOGGLE);
- #if defined(HAVE_CG) || defined(HAVE_GLSL) || defined(HAVE_SLANG) || defined(HAVE_HLSL)
- if (settings->bools.video_shader_watch_files)
- {
- static rarch_timer_t timer = {0};
- static bool need_to_apply = false;
- if (video_shader_check_for_changes())
- {
- need_to_apply = true;
- if (!timer.timer_begin)
- {
- uint64_t current_usec = cpu_features_get_time_usec();
- RARCH_TIMER_BEGIN_NEW_TIME_USEC(timer,
- current_usec,
- SHADER_FILE_WATCH_DELAY_MSEC * 1000);
- timer.timer_begin = true;
- timer.timer_end = false;
- }
- }
- /* If a file is modified atomically (moved/renamed from a different file),
- * we have no idea how long that might take.
- * If we're trying to re-apply shaders immediately after changes are made
- * to the original file(s), the filesystem might be in an in-between
- * state where the new file hasn't been moved over yet and the original
- * file was already deleted. This leaves us no choice but to wait an
- * arbitrary amount of time and hope for the best.
- */
- if (need_to_apply)
- {
- RARCH_TIMER_TICK(timer, current_time);
- if (!timer.timer_end && RARCH_TIMER_HAS_EXPIRED(timer))
- {
- RARCH_TIMER_END(timer);
- need_to_apply = false;
- command_event(CMD_EVENT_SHADERS_APPLY_CHANGES, NULL);
- }
- }
- }
- if ( settings->uints.video_shader_delay &&
- !p_rarch->shader_delay_timer.timer_end)
- {
- if (!p_rarch->shader_delay_timer.timer_begin)
- {
- uint64_t current_usec = cpu_features_get_time_usec();
- RARCH_TIMER_BEGIN_NEW_TIME_USEC(
- p_rarch->shader_delay_timer,
- current_usec,
- settings->uints.video_shader_delay * 1000);
- p_rarch->shader_delay_timer.timer_begin = true;
- p_rarch->shader_delay_timer.timer_end = false;
- }
- else
- {
- RARCH_TIMER_TICK(p_rarch->shader_delay_timer, current_time);
- if (RARCH_TIMER_HAS_EXPIRED(p_rarch->shader_delay_timer))
- {
- RARCH_TIMER_END(p_rarch->shader_delay_timer);
- {
- const char *preset = retroarch_get_shader_preset();
- enum rarch_shader_type type = video_shader_parse_type(preset);
- retroarch_apply_shader(type, preset, false);
- }
- }
- }
- }
- #endif
- return RUNLOOP_STATE_ITERATE;
- }
- /**
- * runloop_iterate:
- *
- * Run Libretro core in RetroArch for one frame.
- *
- * Returns: 0 on success, 1 if we have to wait until
- * button input in order to wake up the loop,
- * -1 if we forcibly quit out of the RetroArch iteration loop.
- **/
- int runloop_iterate(void)
- {
- unsigned i;
- struct rarch_state *p_rarch = &rarch_st;
- settings_t *settings = p_rarch->configuration_settings;
- float fastforward_ratio = settings->floats.fastforward_ratio;
- unsigned video_frame_delay = settings->uints.video_frame_delay;
- bool vrr_runloop_enable = settings->bools.vrr_runloop_enable;
- unsigned max_users = p_rarch->input_driver_max_users;
- retro_time_t current_time = cpu_features_get_time_usec();
- bool core_paused = p_rarch->runloop_paused || (settings->bools.menu_pause_libretro && p_rarch->menu_driver_alive);
- #ifdef HAVE_DISCORD
- discord_state_t *discord_st = &p_rarch->discord_st;
- if (discord_is_inited)
- {
- Discord_RunCallbacks();
- Discord_UpdateConnection();
- }
- #endif
- if (p_rarch->runloop_frame_time.callback)
- {
- /* Updates frame timing if frame timing callback is in use by the core.
- * Limits frame time if fast forward ratio throttle is enabled. */
- retro_usec_t runloop_last_frame_time = p_rarch->runloop_frame_time_last;
- retro_time_t current = current_time;
- bool is_locked_fps = (p_rarch->runloop_paused
- || p_rarch->input_driver_nonblock_state)
- | !!p_rarch->recording_data;
- retro_time_t delta = (!runloop_last_frame_time || is_locked_fps)
- ? p_rarch->runloop_frame_time.reference
- : (current - runloop_last_frame_time);
- if (is_locked_fps)
- p_rarch->runloop_frame_time_last = 0;
- else
- {
- float slowmotion_ratio =
- settings->floats.slowmotion_ratio;
- p_rarch->runloop_frame_time_last = current;
- if (p_rarch->runloop_slowmotion)
- delta /= slowmotion_ratio;
- }
- if (!core_paused)
- p_rarch->runloop_frame_time.callback(delta);
- }
- /* Update audio buffer occupancy if buffer status
- * callback is in use by the core */
- if (p_rarch->runloop_audio_buffer_status.callback)
- {
- bool audio_buf_active = false;
- unsigned audio_buf_occupancy = 0;
- bool audio_buf_underrun = false;
- if (!(p_rarch->runloop_paused ||
- !p_rarch->audio_driver_active ||
- !p_rarch->audio_driver_output_samples_buf) &&
- p_rarch->current_audio->write_avail &&
- p_rarch->audio_driver_context_audio_data &&
- p_rarch->audio_driver_buffer_size)
- {
- size_t audio_buf_avail;
- if ((audio_buf_avail = p_rarch->current_audio->write_avail(
- p_rarch->audio_driver_context_audio_data)) > p_rarch->audio_driver_buffer_size)
- audio_buf_avail = p_rarch->audio_driver_buffer_size;
- audio_buf_occupancy = (unsigned)(100 - (audio_buf_avail * 100) /
- p_rarch->audio_driver_buffer_size);
- /* Elsewhere, we standardise on a 'low water mark'
- * of 25% of the total audio buffer size - use
- * the same metric here (can be made more sophisticated
- * if required - i.e. determine buffer occupancy in
- * terms of usec, and weigh this against the expected
- * frame time) */
- audio_buf_underrun = audio_buf_occupancy < 25;
- audio_buf_active = true;
- }
- if (!core_paused)
- p_rarch->runloop_audio_buffer_status.callback(
- audio_buf_active, audio_buf_occupancy, audio_buf_underrun);
- }
- switch ((enum runloop_state)runloop_check_state(p_rarch,
- settings, current_time))
- {
- case RUNLOOP_STATE_QUIT:
- p_rarch->frame_limit_last_time = 0.0;
- p_rarch->runloop_core_running = false;
- command_event(CMD_EVENT_QUIT, NULL);
- return -1;
- case RUNLOOP_STATE_POLLED_AND_SLEEP:
- #ifdef HAVE_NETWORKING
- /* FIXME: This is an ugly way to tell Netplay this... */
- netplay_driver_ctl(RARCH_NETPLAY_CTL_PAUSE, NULL);
- #endif
- #if defined(HAVE_COCOATOUCH)
- if (!p_rarch->main_ui_companion_is_on_foreground)
- #endif
- retro_sleep(10);
- return 1;
- case RUNLOOP_STATE_END:
- #ifdef HAVE_NETWORKING
- /* FIXME: This is an ugly way to tell Netplay this... */
- if (settings->bools.menu_pause_libretro &&
- netplay_driver_ctl(RARCH_NETPLAY_CTL_IS_ENABLED, NULL)
- )
- netplay_driver_ctl(RARCH_NETPLAY_CTL_PAUSE, NULL);
- #endif
- goto end;
- case RUNLOOP_STATE_MENU_ITERATE:
- #ifdef HAVE_NETWORKING
- /* FIXME: This is an ugly way to tell Netplay this... */
- netplay_driver_ctl(RARCH_NETPLAY_CTL_PAUSE, NULL);
- #endif
- return 0;
- case RUNLOOP_STATE_ITERATE:
- p_rarch->runloop_core_running = true;
- break;
- }
- #ifdef HAVE_THREADS
- if (p_rarch->runloop_autosave)
- autosave_lock();
- #endif
- #ifdef HAVE_BSV_MOVIE
- /* Used for rewinding while playback/record. */
- if (p_rarch->bsv_movie_state_handle)
- p_rarch->bsv_movie_state_handle->frame_pos[p_rarch->bsv_movie_state_handle->frame_ptr]
- = intfstream_tell(p_rarch->bsv_movie_state_handle->file);
- #endif
- if ( p_rarch->camera_cb.caps &&
- p_rarch->camera_driver &&
- p_rarch->camera_driver->poll &&
- p_rarch->camera_data)
- p_rarch->camera_driver->poll(p_rarch->camera_data,
- p_rarch->camera_cb.frame_raw_framebuffer,
- p_rarch->camera_cb.frame_opengl_texture);
- /* Update binds for analog dpad modes. */
- for (i = 0; i < max_users; i++)
- {
- enum analog_dpad_mode dpad_mode = (enum analog_dpad_mode)settings->uints.input_analog_dpad_mode[i];
- if (dpad_mode != ANALOG_DPAD_NONE)
- {
- unsigned k;
- struct retro_keybind *general_binds = input_config_binds[i];
- struct retro_keybind *auto_binds = input_autoconf_binds[i];
- unsigned x_plus = RARCH_ANALOG_RIGHT_X_PLUS;
- unsigned y_plus = RARCH_ANALOG_RIGHT_Y_PLUS;
- unsigned x_minus = RARCH_ANALOG_RIGHT_X_MINUS;
- unsigned y_minus = RARCH_ANALOG_RIGHT_Y_MINUS;
- /* Push analog to D-Pad mappings to binds. */
- if ((dpad_mode) == ANALOG_DPAD_LSTICK)
- {
- x_plus = RARCH_ANALOG_LEFT_X_PLUS;
- y_plus = RARCH_ANALOG_LEFT_Y_PLUS;
- x_minus = RARCH_ANALOG_LEFT_X_MINUS;
- y_minus = RARCH_ANALOG_LEFT_Y_MINUS;
- }
- for (k = RETRO_DEVICE_ID_JOYPAD_UP; k <= RETRO_DEVICE_ID_JOYPAD_RIGHT; k++)
- {
- (auto_binds)[k].orig_joyaxis = (auto_binds)[k].joyaxis;
- (general_binds)[k].orig_joyaxis = (general_binds)[k].joyaxis;
- }
- if (!INHERIT_JOYAXIS(auto_binds))
- {
- unsigned j = x_plus + 3;
- /* Inherit joyaxis from analogs. */
- for (k = RETRO_DEVICE_ID_JOYPAD_UP; k <= RETRO_DEVICE_ID_JOYPAD_RIGHT; k++)
- (auto_binds)[k].joyaxis = (auto_binds)[j--].joyaxis;
- }
- if (!INHERIT_JOYAXIS(general_binds))
- {
- unsigned j = x_plus + 3;
- /* Inherit joyaxis from analogs. */
- for (k = RETRO_DEVICE_ID_JOYPAD_UP; k <= RETRO_DEVICE_ID_JOYPAD_RIGHT; k++)
- (general_binds)[k].joyaxis = (general_binds)[j--].joyaxis;
- }
- }
- }
- if ((video_frame_delay > 0) && !p_rarch->input_driver_nonblock_state)
- retro_sleep(video_frame_delay);
- {
- #ifdef HAVE_RUNAHEAD
- unsigned run_ahead_num_frames = settings->uints.run_ahead_frames;
- /* Run Ahead Feature replaces the call to core_run in this loop */
- bool want_runahead = settings->bools.run_ahead_enabled && run_ahead_num_frames > 0;
- #ifdef HAVE_NETWORKING
- want_runahead = want_runahead && !netplay_driver_ctl(RARCH_NETPLAY_CTL_IS_ENABLED, NULL);
- #endif
- if (want_runahead)
- do_runahead(
- p_rarch,
- run_ahead_num_frames,
- settings->bools.run_ahead_secondary_instance);
- else
- #endif
- core_run();
- }
- /* Increment runtime tick counter after each call to
- * core_run() or run_ahead() */
- p_rarch->libretro_core_runtime_usec += rarch_core_runtime_tick(
- p_rarch, current_time);
- #ifdef HAVE_CHEEVOS
- if (settings->bools.cheevos_enable)
- rcheevos_test();
- #endif
- #ifdef HAVE_CHEATS
- cheat_manager_apply_retro_cheats();
- #endif
- #ifdef HAVE_DISCORD
- if (discord_is_inited && discord_st->ready)
- discord_update(DISCORD_PRESENCE_GAME);
- #endif
- for (i = 0; i < max_users; i++)
- {
- unsigned j;
- enum analog_dpad_mode dpad_mode = (enum analog_dpad_mode)settings->uints.input_analog_dpad_mode[i];
- /* Restores analog D-pad binds temporarily overridden. */
- if (dpad_mode != ANALOG_DPAD_NONE)
- {
- struct retro_keybind *general_binds = input_config_binds[i];
- struct retro_keybind *auto_binds = input_autoconf_binds[i];
- for (j = RETRO_DEVICE_ID_JOYPAD_UP; j <= RETRO_DEVICE_ID_JOYPAD_RIGHT; j++)
- {
- (auto_binds)[j].joyaxis = (auto_binds)[j].orig_joyaxis;
- (general_binds)[j].joyaxis = (general_binds)[j].orig_joyaxis;
- }
- }
- }
- #ifdef HAVE_BSV_MOVIE
- if (p_rarch->bsv_movie_state_handle)
- {
- p_rarch->bsv_movie_state_handle->frame_ptr =
- (p_rarch->bsv_movie_state_handle->frame_ptr + 1)
- & p_rarch->bsv_movie_state_handle->frame_mask;
- p_rarch->bsv_movie_state_handle->first_rewind =
- !p_rarch->bsv_movie_state_handle->did_rewind;
- p_rarch->bsv_movie_state_handle->did_rewind = false;
- }
- #endif
- #ifdef HAVE_THREADS
- if (p_rarch->runloop_autosave)
- autosave_unlock();
- #endif
- /* Condition for max speed x0.0 when vrr_runloop is off to skip that part */
- if (!(fastforward_ratio || vrr_runloop_enable))
- return 0;
- end:
- if (vrr_runloop_enable)
- {
- struct retro_system_av_info *av_info = &p_rarch->video_driver_av_info;
- bool audio_sync = settings->bools.audio_sync;
- /* Sync on video only, block audio later. */
- if (p_rarch->fastforward_after_frames && audio_sync)
- {
- if (p_rarch->fastforward_after_frames == 1)
- {
- /* Nonblocking audio */
- if (p_rarch->audio_driver_active &&
- p_rarch->audio_driver_context_audio_data)
- p_rarch->current_audio->set_nonblock_state(
- p_rarch->audio_driver_context_audio_data, true);
- p_rarch->audio_driver_chunk_size =
- p_rarch->audio_driver_chunk_nonblock_size;
- }
- p_rarch->fastforward_after_frames++;
- if (p_rarch->fastforward_after_frames == 6)
- {
- /* Blocking audio */
- if (p_rarch->audio_driver_active &&
- p_rarch->audio_driver_context_audio_data)
- p_rarch->current_audio->set_nonblock_state(
- p_rarch->audio_driver_context_audio_data,
- audio_sync ? false : true);
- p_rarch->audio_driver_chunk_size =
- p_rarch->audio_driver_chunk_block_size;
- p_rarch->fastforward_after_frames = 0;
- }
- }
- /* Fast Forward for max speed x0.0 */
- if (!fastforward_ratio && p_rarch->runloop_fastmotion)
- return 0;
- p_rarch->frame_limit_minimum_time =
- (retro_time_t)roundf(1000000.0f / (av_info->timing.fps *
- (p_rarch->runloop_fastmotion
- ? fastforward_ratio : 1.0f)));
- }
- {
- retro_time_t to_sleep_ms = (
- (p_rarch->frame_limit_last_time + p_rarch->frame_limit_minimum_time)
- - cpu_features_get_time_usec()) / 1000;
- if (to_sleep_ms > 0)
- {
- unsigned sleep_ms = (unsigned)to_sleep_ms;
- /* Combat jitter a bit. */
- p_rarch->frame_limit_last_time += p_rarch->frame_limit_minimum_time;
- if (sleep_ms > 0)
- #if defined(HAVE_COCOATOUCH)
- if (!p_rarch->main_ui_companion_is_on_foreground)
- #endif
- retro_sleep(sleep_ms);
- return 1;
- }
- }
- p_rarch->frame_limit_last_time = cpu_features_get_time_usec();
- return 0;
- }
- rarch_system_info_t *runloop_get_system_info(void)
- {
- struct rarch_state *p_rarch = &rarch_st;
- return &p_rarch->runloop_system;
- }
- struct retro_system_info *runloop_get_libretro_system_info(void)
- {
- struct rarch_state *p_rarch = &rarch_st;
- return &p_rarch->runloop_system.info;
- }
- void retroarch_force_video_driver_fallback(const char *driver)
- {
- struct rarch_state *p_rarch = &rarch_st;
- settings_t *settings = p_rarch->configuration_settings;
- ui_msg_window_t *msg_window = NULL;
- configuration_set_string(settings,
- settings->arrays.video_driver,
- driver);
- command_event(CMD_EVENT_MENU_SAVE_CURRENT_CONFIG, NULL);
- #if defined(_WIN32) && !defined(_XBOX) && !defined(__WINRT__) && !defined(WINAPI_FAMILY)
- /* UI companion driver is not inited yet, just call into it directly */
- msg_window = &ui_msg_window_win32;
- #endif
- if (msg_window)
- {
- char text[PATH_MAX_LENGTH];
- ui_msg_window_state window_state;
- char *title = strdup(msg_hash_to_str(MSG_ERROR));
- text[0] = '\0';
- snprintf(text, sizeof(text),
- msg_hash_to_str(MENU_ENUM_LABEL_VALUE_VIDEO_DRIVER_FALLBACK),
- driver);
- window_state.buttons = UI_MSG_WINDOW_OK;
- window_state.text = strdup(text);
- window_state.title = title;
- window_state.window = NULL;
- msg_window->error(&window_state);
- free(title);
- }
- exit(1);
- }
- enum retro_language rarch_get_language_from_iso(const char *iso639)
- {
- unsigned i;
- enum retro_language lang = RETRO_LANGUAGE_ENGLISH;
- struct lang_pair
- {
- const char *iso639;
- enum retro_language lang;
- };
- const struct lang_pair pairs[] =
- {
- {"en", RETRO_LANGUAGE_ENGLISH},
- {"ja", RETRO_LANGUAGE_JAPANESE},
- {"fr", RETRO_LANGUAGE_FRENCH},
- {"es", RETRO_LANGUAGE_SPANISH},
- {"de", RETRO_LANGUAGE_GERMAN},
- {"it", RETRO_LANGUAGE_ITALIAN},
- {"nl", RETRO_LANGUAGE_DUTCH},
- {"pt_BR", RETRO_LANGUAGE_PORTUGUESE_BRAZIL},
- {"pt_PT", RETRO_LANGUAGE_PORTUGUESE_PORTUGAL},
- {"pt", RETRO_LANGUAGE_PORTUGUESE_PORTUGAL},
- {"ru", RETRO_LANGUAGE_RUSSIAN},
- {"ko", RETRO_LANGUAGE_KOREAN},
- {"zh_CN", RETRO_LANGUAGE_CHINESE_SIMPLIFIED},
- {"zh_SG", RETRO_LANGUAGE_CHINESE_SIMPLIFIED},
- {"zh_HK", RETRO_LANGUAGE_CHINESE_TRADITIONAL},
- {"zh_TW", RETRO_LANGUAGE_CHINESE_TRADITIONAL},
- {"zh", RETRO_LANGUAGE_CHINESE_SIMPLIFIED},
- {"eo", RETRO_LANGUAGE_ESPERANTO},
- {"pl", RETRO_LANGUAGE_POLISH},
- {"vi", RETRO_LANGUAGE_VIETNAMESE},
- {"ar", RETRO_LANGUAGE_ARABIC},
- {"el", RETRO_LANGUAGE_GREEK},
- };
- if (string_is_empty(iso639))
- return lang;
- for (i = 0; i < ARRAY_SIZE(pairs); i++)
- {
- if (strcasestr(iso639, pairs[i].iso639))
- {
- lang = pairs[i].lang;
- break;
- }
- }
- return lang;
- }
- void rarch_favorites_init(void)
- {
- struct rarch_state *p_rarch = &rarch_st;
- settings_t *settings = p_rarch->configuration_settings;
- int content_favorites_size = settings ? settings->ints.content_favorites_size : 0;
- const char *path_content_favorites = settings ? settings->paths.path_content_favorites : NULL;
- bool playlist_sort_alphabetical = settings ? settings->bools.playlist_sort_alphabetical : false;
- playlist_config_t playlist_config;
- enum playlist_sort_mode current_sort_mode;
- playlist_config.capacity = COLLECTION_SIZE;
- playlist_config.old_format = settings ? settings->bools.playlist_use_old_format : false;
- playlist_config.compress = settings ? settings->bools.playlist_compression : false;
- playlist_config.fuzzy_archive_match = settings ? settings->bools.playlist_fuzzy_archive_match : false;
- playlist_config_set_base_content_directory(&playlist_config, NULL);
- if (!settings)
- return;
- if (content_favorites_size >= 0)
- playlist_config.capacity = (size_t)content_favorites_size;
- rarch_favorites_deinit();
- RARCH_LOG("[Playlist]: %s: [%s].\n",
- msg_hash_to_str(MSG_LOADING_FAVORITES_FILE),
- path_content_favorites);
- playlist_config_set_path(&playlist_config, path_content_favorites);
- g_defaults.content_favorites = playlist_init(&playlist_config);
- /* Get current per-playlist sort mode */
- current_sort_mode = playlist_get_sort_mode(g_defaults.content_favorites);
- /* Ensure that playlist is sorted alphabetically,
- * if required */
- if ((playlist_sort_alphabetical && (current_sort_mode == PLAYLIST_SORT_MODE_DEFAULT)) ||
- (current_sort_mode == PLAYLIST_SORT_MODE_ALPHABETICAL))
- playlist_qsort(g_defaults.content_favorites);
- }
- void rarch_favorites_deinit(void)
- {
- if (!g_defaults.content_favorites)
- return;
- playlist_write_file(g_defaults.content_favorites);
- playlist_free(g_defaults.content_favorites);
- g_defaults.content_favorites = NULL;
- }
- /* Libretro core loader */
- static void retro_run_null(void) { }
- static void retro_frame_null(const void *data, unsigned width,
- unsigned height, size_t pitch) { }
- static void retro_input_poll_null(void) { }
- static int16_t core_input_state_poll_late(unsigned port,
- unsigned device, unsigned idx, unsigned id)
- {
- struct rarch_state *p_rarch = &rarch_st;
- if (!p_rarch->current_core.input_polled)
- input_driver_poll();
- p_rarch->current_core.input_polled = true;
- return input_state(port, device, idx, id);
- }
- static retro_input_state_t core_input_state_poll_return_cb(void)
- {
- struct rarch_state *p_rarch = &rarch_st;
- const enum poll_type_override_t
- core_poll_type_override = p_rarch->core_poll_type_override;
- unsigned new_poll_type = (core_poll_type_override > POLL_TYPE_OVERRIDE_DONTCARE)
- ? (core_poll_type_override - 1)
- : p_rarch->current_core.poll_type;
- if (new_poll_type == POLL_TYPE_LATE)
- return core_input_state_poll_late;
- return input_state;
- }
- static void core_input_state_poll_maybe(void)
- {
- struct rarch_state *p_rarch = &rarch_st;
- const enum poll_type_override_t
- core_poll_type_override = p_rarch->core_poll_type_override;
- unsigned new_poll_type = (core_poll_type_override > POLL_TYPE_OVERRIDE_DONTCARE)
- ? (core_poll_type_override - 1)
- : p_rarch->current_core.poll_type;
- if (new_poll_type == POLL_TYPE_NORMAL)
- input_driver_poll();
- }
- /**
- * core_init_libretro_cbs:
- * @data : pointer to retro_callbacks object
- *
- * Initializes libretro callbacks, and binds the libretro callbacks
- * to default callback functions.
- **/
- static bool core_init_libretro_cbs(
- struct rarch_state *p_rarch,
- struct retro_callbacks *cbs)
- {
- retro_input_state_t state_cb = core_input_state_poll_return_cb();
- p_rarch->current_core.retro_set_video_refresh(video_driver_frame);
- p_rarch->current_core.retro_set_audio_sample(audio_driver_sample);
- p_rarch->current_core.retro_set_audio_sample_batch(audio_driver_sample_batch);
- p_rarch->current_core.retro_set_input_state(state_cb);
- p_rarch->current_core.retro_set_input_poll(core_input_state_poll_maybe);
- core_set_default_callbacks(cbs);
- #ifdef HAVE_NETWORKING
- if (!netplay_driver_ctl(RARCH_NETPLAY_CTL_IS_DATA_INITED, NULL))
- return true;
- core_set_netplay_callbacks();
- #endif
- return true;
- }
- /**
- * core_set_default_callbacks:
- * @data : pointer to retro_callbacks object
- *
- * Binds the libretro callbacks to default callback functions.
- **/
- static bool core_set_default_callbacks(struct retro_callbacks *cbs)
- {
- retro_input_state_t state_cb = core_input_state_poll_return_cb();
- cbs->frame_cb = video_driver_frame;
- cbs->sample_cb = audio_driver_sample;
- cbs->sample_batch_cb = audio_driver_sample_batch;
- cbs->state_cb = state_cb;
- cbs->poll_cb = input_driver_poll;
- return true;
- }
- #ifdef HAVE_REWIND
- /**
- * core_set_rewind_callbacks:
- *
- * Sets the audio sampling callbacks based on whether or not
- * rewinding is currently activated.
- **/
- bool core_set_rewind_callbacks(void)
- {
- struct rarch_state *p_rarch = &rarch_st;
- struct state_manager_rewind_state
- *rewind_st = &p_rarch->rewind_st;
- if (rewind_st->frame_is_reversed)
- {
- p_rarch->current_core.retro_set_audio_sample(audio_driver_sample_rewind);
- p_rarch->current_core.retro_set_audio_sample_batch(audio_driver_sample_batch_rewind);
- }
- else
- {
- p_rarch->current_core.retro_set_audio_sample(audio_driver_sample);
- p_rarch->current_core.retro_set_audio_sample_batch(audio_driver_sample_batch);
- }
- return true;
- }
- #endif
- #ifdef HAVE_NETWORKING
- /**
- * core_set_netplay_callbacks:
- *
- * Set the I/O callbacks to use netplay's interceding callback system. Should
- * only be called while initializing netplay.
- **/
- bool core_set_netplay_callbacks(void)
- {
- struct rarch_state *p_rarch = &rarch_st;
- /* Force normal poll type for netplay. */
- p_rarch->current_core.poll_type = POLL_TYPE_NORMAL;
- /* And use netplay's interceding callbacks */
- p_rarch->current_core.retro_set_video_refresh(video_frame_net);
- p_rarch->current_core.retro_set_audio_sample(audio_sample_net);
- p_rarch->current_core.retro_set_audio_sample_batch(audio_sample_batch_net);
- p_rarch->current_core.retro_set_input_state(input_state_net);
- return true;
- }
- /**
- * core_unset_netplay_callbacks
- *
- * Unset the I/O callbacks from having used netplay's interceding callback
- * system. Should only be called while uninitializing netplay.
- */
- bool core_unset_netplay_callbacks(void)
- {
- struct retro_callbacks cbs;
- struct rarch_state *p_rarch = &rarch_st;
- if (!core_set_default_callbacks(&cbs))
- return false;
- p_rarch->current_core.retro_set_video_refresh(cbs.frame_cb);
- p_rarch->current_core.retro_set_audio_sample(cbs.sample_cb);
- p_rarch->current_core.retro_set_audio_sample_batch(cbs.sample_batch_cb);
- p_rarch->current_core.retro_set_input_state(cbs.state_cb);
- return true;
- }
- #endif
- bool core_set_cheat(retro_ctx_cheat_info_t *info)
- {
- struct rarch_state *p_rarch = &rarch_st;
- p_rarch->current_core.retro_cheat_set(info->index, info->enabled, info->code);
- return true;
- }
- bool core_reset_cheat(void)
- {
- struct rarch_state *p_rarch = &rarch_st;
- p_rarch->current_core.retro_cheat_reset();
- return true;
- }
- bool core_set_poll_type(unsigned type)
- {
- struct rarch_state *p_rarch = &rarch_st;
- p_rarch->current_core.poll_type = type;
- return true;
- }
- bool core_set_controller_port_device(retro_ctx_controller_info_t *pad)
- {
- struct rarch_state *p_rarch = &rarch_st;
- if (!pad)
- return false;
- #ifdef HAVE_RUNAHEAD
- remember_controller_port_device(p_rarch, pad->port, pad->device);
- #endif
- p_rarch->current_core.retro_set_controller_port_device(pad->port, pad->device);
- return true;
- }
- bool core_get_memory(retro_ctx_memory_info_t *info)
- {
- struct rarch_state *p_rarch = &rarch_st;
- if (!info)
- return false;
- info->size = p_rarch->current_core.retro_get_memory_size(info->id);
- info->data = p_rarch->current_core.retro_get_memory_data(info->id);
- return true;
- }
- bool core_load_game(retro_ctx_load_content_info_t *load_info)
- {
- bool contentless = false;
- bool is_inited = false;
- bool game_loaded = false;
- struct rarch_state *p_rarch = &rarch_st;
- video_driver_set_cached_frame_ptr(NULL);
- #ifdef HAVE_RUNAHEAD
- set_load_content_info(p_rarch, load_info);
- clear_controller_port_map(p_rarch);
- #endif
- content_get_status(&contentless, &is_inited);
- set_save_state_in_background(false);
- if (load_info && load_info->special)
- game_loaded = p_rarch->current_core.retro_load_game_special(
- load_info->special->id, load_info->info, load_info->content->size);
- else if (load_info && !string_is_empty(load_info->content->elems[0].data))
- game_loaded = p_rarch->current_core.retro_load_game(load_info->info);
- else if (contentless)
- game_loaded = p_rarch->current_core.retro_load_game(NULL);
- p_rarch->current_core.game_loaded = game_loaded;
- return game_loaded;
- }
- bool core_get_system_info(struct retro_system_info *system)
- {
- struct rarch_state *p_rarch = &rarch_st;
- if (!system)
- return false;
- p_rarch->current_core.retro_get_system_info(system);
- return true;
- }
- bool core_unserialize(retro_ctx_serialize_info_t *info)
- {
- struct rarch_state *p_rarch = &rarch_st;
- if (!info || !p_rarch->current_core.retro_unserialize(info->data_const, info->size))
- return false;
- #if HAVE_NETWORKING
- netplay_driver_ctl(RARCH_NETPLAY_CTL_LOAD_SAVESTATE, info);
- #endif
- return true;
- }
- bool core_serialize(retro_ctx_serialize_info_t *info)
- {
- struct rarch_state *p_rarch = &rarch_st;
- if (!info || !p_rarch->current_core.retro_serialize(info->data, info->size))
- return false;
- return true;
- }
- bool core_serialize_size(retro_ctx_size_info_t *info)
- {
- struct rarch_state *p_rarch = &rarch_st;
- if (!info)
- return false;
- info->size = p_rarch->current_core.retro_serialize_size();
- return true;
- }
- uint64_t core_serialization_quirks(void)
- {
- struct rarch_state *p_rarch = &rarch_st;
- return p_rarch->current_core.serialization_quirks_v;
- }
- bool core_reset(void)
- {
- struct rarch_state *p_rarch = &rarch_st;
- video_driver_set_cached_frame_ptr(NULL);
- p_rarch->current_core.retro_reset();
- return true;
- }
- static bool core_unload_game(struct rarch_state *p_rarch)
- {
- video_driver_free_hw_context(p_rarch);
- video_driver_set_cached_frame_ptr(NULL);
- if (p_rarch->current_core.game_loaded)
- {
- RARCH_LOG("[Core]: Unloading game..\n");
- p_rarch->current_core.retro_unload_game();
- p_rarch->core_poll_type_override = POLL_TYPE_OVERRIDE_DONTCARE;
- p_rarch->current_core.game_loaded = false;
- }
- audio_driver_stop(p_rarch);
- return true;
- }
- bool core_run(void)
- {
- struct rarch_state
- *p_rarch = &rarch_st;
- struct retro_core_t *
- current_core = &p_rarch->current_core;
- const enum poll_type_override_t
- core_poll_type_override = p_rarch->core_poll_type_override;
- unsigned new_poll_type = (core_poll_type_override != POLL_TYPE_OVERRIDE_DONTCARE)
- ? (core_poll_type_override - 1)
- : current_core->poll_type;
- bool early_polling = new_poll_type == POLL_TYPE_EARLY;
- bool late_polling = new_poll_type == POLL_TYPE_LATE;
- #ifdef HAVE_NETWORKING
- bool netplay_preframe = netplay_driver_ctl(
- RARCH_NETPLAY_CTL_PRE_FRAME, NULL);
- if (!netplay_preframe)
- {
- /* Paused due to netplay. We must poll and display something so that a
- * netplay peer pausing doesn't just hang. */
- input_driver_poll();
- video_driver_cached_frame();
- return true;
- }
- #endif
- if (early_polling)
- input_driver_poll();
- else if (late_polling)
- current_core->input_polled = false;
- current_core->retro_run();
- if (late_polling && !current_core->input_polled)
- input_driver_poll();
- #ifdef HAVE_NETWORKING
- netplay_driver_ctl(RARCH_NETPLAY_CTL_POST_FRAME, NULL);
- #endif
- return true;
- }
- static bool core_verify_api_version(struct rarch_state *p_rarch)
- {
- unsigned api_version = p_rarch->current_core.retro_api_version();
- RARCH_LOG("%s: %u\n%s %s: %u\n",
- msg_hash_to_str(MSG_VERSION_OF_LIBRETRO_API),
- api_version,
- FILE_PATH_LOG_INFO,
- msg_hash_to_str(MSG_COMPILED_AGAINST_API),
- RETRO_API_VERSION
- );
- if (api_version != RETRO_API_VERSION)
- {
- RARCH_WARN("%s\n", msg_hash_to_str(MSG_LIBRETRO_ABI_BREAK));
- return false;
- }
- return true;
- }
- static bool core_load(
- struct rarch_state *p_rarch,
- unsigned poll_type_behavior)
- {
- p_rarch->current_core.poll_type = poll_type_behavior;
- if (!core_verify_api_version(p_rarch))
- return false;
- if (!core_init_libretro_cbs(p_rarch,
- &p_rarch->retro_ctx))
- return false;
- p_rarch->current_core.retro_get_system_av_info(
- &p_rarch->video_driver_av_info);
- return true;
- }
- bool core_has_set_input_descriptor(void)
- {
- struct rarch_state *p_rarch = &rarch_st;
- return p_rarch->current_core.has_set_input_descriptors;
- }
- #if defined(HAVE_RUNAHEAD)
- static void core_free_retro_game_info(struct retro_game_info *dest)
- {
- if (!dest)
- return;
- if (dest->path)
- free((void*)dest->path);
- if (dest->data)
- free((void*)dest->data);
- if (dest->meta)
- free((void*)dest->meta);
- dest->path = NULL;
- dest->data = NULL;
- dest->meta = NULL;
- }
- #endif
- unsigned int retroarch_get_rotation(void)
- {
- struct rarch_state *p_rarch = &rarch_st;
- settings_t *settings = p_rarch->configuration_settings;
- unsigned video_rotation = settings->uints.video_rotation;
- return video_rotation + p_rarch->runloop_system.rotation;
- }
- #ifdef HAVE_ACCESSIBILITY
- static bool accessibility_speak_priority(
- struct rarch_state *p_rarch,
- const char* speak_text, int priority)
- {
- settings_t *settings = p_rarch->configuration_settings;
- RARCH_LOG("Spoke: %s\n", speak_text);
- if (is_accessibility_enabled(p_rarch))
- {
- frontend_ctx_driver_t *frontend = p_rarch->current_frontend_ctx;
- if (frontend && frontend->accessibility_speak)
- {
- int speed = settings->uints.accessibility_narrator_speech_speed;
- return frontend->accessibility_speak(speed, speak_text,
- priority);
- }
- RARCH_LOG("Platform not supported for accessibility.\n");
- /* The following method is a fallback for other platforms to use the
- AI Service url to do the TTS. However, since the playback is done
- via the audio mixer, which only processes the audio while the
- core is running, this playback method won't work. When the audio
- mixer can handle playing streams while the core is paused, then
- we can use this. */
- #if 0
- #if defined(HAVE_NETWORKING)
- return accessibility_speak_ai_service(speak_text, voice, priority);
- #endif
- #endif
- }
- return true;
- }
- #ifdef HAVE_TRANSLATE
- static bool is_narrator_running(struct rarch_state *p_rarch)
- {
- if (is_accessibility_enabled(p_rarch))
- {
- frontend_ctx_driver_t *frontend = p_rarch->current_frontend_ctx;
- if (frontend && frontend->is_narrator_running)
- return frontend->is_narrator_running();
- }
- return true;
- }
- #endif
- #endif
- /* Creates folder and core options stub file for subsequent runs */
- bool core_options_create_override(bool game_specific)
- {
- char options_path[PATH_MAX_LENGTH];
- config_file_t *conf = NULL;
- struct rarch_state *p_rarch = &rarch_st;
- options_path[0] = '\0';
- /* Sanity check - cannot create a folder-specific
- * override if a game-specific override is
- * already active */
- if (!game_specific && p_rarch->runloop_game_options_active)
- goto error;
- /* Get options file path (either game-specific or folder-specific) */
- if (game_specific)
- {
- if (!retroarch_validate_game_options(options_path,
- sizeof(options_path), true))
- goto error;
- }
- else
- if (!retroarch_validate_folder_options(options_path,
- sizeof(options_path), true))
- goto error;
- /* Open config file */
- if (!(conf = config_file_new_from_path_to_string(options_path)))
- if (!(conf = config_file_new_alloc()))
- goto error;
- /* Write config file */
- core_option_manager_flush(conf, p_rarch->runloop_core_options);
- if (config_file_write(conf, options_path, true))
- {
- runloop_msg_queue_push(
- msg_hash_to_str(MSG_CORE_OPTIONS_FILE_CREATED_SUCCESSFULLY),
- 1, 100, true,
- NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO);
- path_set(RARCH_PATH_CORE_OPTIONS, options_path);
- p_rarch->runloop_game_options_active = game_specific;
- p_rarch->runloop_folder_options_active = !game_specific;
- }
- else
- goto error;
- config_file_free(conf);
- return true;
- error:
- runloop_msg_queue_push(
- msg_hash_to_str(MSG_ERROR_SAVING_CORE_OPTIONS_FILE),
- 1, 100, true,
- NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO);
- if (conf)
- config_file_free(conf);
- return false;
- }
- bool core_options_remove_override(bool game_specific)
- {
- char new_options_path[PATH_MAX_LENGTH];
- struct rarch_state *p_rarch = &rarch_st;
- core_option_manager_t *coreopts = p_rarch->runloop_core_options;
- settings_t *settings = p_rarch->configuration_settings;
- bool per_core_options = !settings->bools.global_core_options;
- const char *path_core_options = settings->paths.path_core_options;
- const char *current_options_path = NULL;
- config_file_t *conf = NULL;
- bool folder_options_active = false;
- new_options_path[0] = '\0';
- /* Sanity check 1 - if there are no core options
- * or no overrides are active, there is nothing to do */
- if (!coreopts ||
- (!p_rarch->runloop_game_options_active &&
- !p_rarch->runloop_folder_options_active))
- return true;
- /* Sanity check 2 - can only remove an override
- * if the specified type is currently active */
- if (game_specific && !p_rarch->runloop_game_options_active)
- goto error;
- /* Get current options file path */
- current_options_path = path_get(RARCH_PATH_CORE_OPTIONS);
- if (string_is_empty(current_options_path))
- goto error;
- /* Remove current options file, if required */
- if (path_is_valid(current_options_path))
- filestream_delete(current_options_path);
- /* Reload any existing 'parent' options file
- * > If we have removed a game-specific config,
- * check whether a folder-specific config
- * exists */
- if (game_specific &&
- retroarch_validate_folder_options(new_options_path,
- sizeof(new_options_path), false) &&
- path_is_valid(new_options_path))
- folder_options_active = true;
- /* > If a folder-specific config does not exist,
- * or we removed it, check whether we have a
- * top-level config file */
- if (!folder_options_active)
- {
- /* Try core-specific options, if enabled */
- if (per_core_options)
- {
- const char *core_name = p_rarch->runloop_system.info.library_name;
- per_core_options = retroarch_validate_per_core_options(
- new_options_path, sizeof(new_options_path), true,
- core_name, core_name);
- }
- /* ...otherwise use global options */
- if (!per_core_options)
- {
- if (!string_is_empty(path_core_options))
- strlcpy(new_options_path,
- path_core_options, sizeof(new_options_path));
- else if (!path_is_empty(RARCH_PATH_CONFIG))
- fill_pathname_resolve_relative(
- new_options_path, path_get(RARCH_PATH_CONFIG),
- FILE_PATH_CORE_OPTIONS_CONFIG, sizeof(new_options_path));
- }
- }
- if (string_is_empty(new_options_path))
- goto error;
- /* > If we have a valid file, load it */
- if (folder_options_active ||
- path_is_valid(new_options_path))
- {
- size_t i, j;
- if (!(conf = config_file_new_from_path_to_string(new_options_path)))
- goto error;
- for (i = 0; i < coreopts->size; i++)
- {
- struct core_option *option = NULL;
- struct config_entry_list *entry = NULL;
- option = (struct core_option*)&coreopts->opts[i];
- if (!option)
- continue;
- entry = config_get_entry(conf, option->key);
- if (!entry || string_is_empty(entry->value))
- continue;
- /* Set current config value from file entry */
- for (j = 0; j < option->vals->size; j++)
- {
- if (string_is_equal(option->vals->elems[j].data, entry->value))
- {
- option->index = j;
- break;
- }
- }
- }
- coreopts->updated = true;
- config_file_free(conf);
- #ifdef HAVE_CHEEVOS
- rcheevos_validate_config_settings();
- #endif
- }
- /* Update runloop status */
- if (folder_options_active)
- {
- path_set(RARCH_PATH_CORE_OPTIONS, new_options_path);
- p_rarch->runloop_game_options_active = false;
- p_rarch->runloop_folder_options_active = true;
- }
- else
- {
- path_clear(RARCH_PATH_CORE_OPTIONS);
- p_rarch->runloop_game_options_active = false;
- p_rarch->runloop_folder_options_active = false;
- strlcpy(coreopts->conf_path, new_options_path,
- sizeof(coreopts->conf_path));
- }
- runloop_msg_queue_push(
- msg_hash_to_str(MSG_CORE_OPTIONS_FILE_REMOVED_SUCCESSFULLY),
- 1, 100, true,
- NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO);
- return true;
- error:
- runloop_msg_queue_push(
- msg_hash_to_str(MSG_ERROR_REMOVING_CORE_OPTIONS_FILE),
- 1, 100, true,
- NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO);
- if (conf)
- config_file_free(conf);
- return false;
- }
- void core_options_reset(void)
- {
- struct rarch_state *p_rarch = &rarch_st;
- core_option_manager_t *coreopts = p_rarch->runloop_core_options;
- size_t i;
- /* If there are no core options, there
- * is nothing to do */
- if (!coreopts || (coreopts->size < 1))
- return;
- for (i = 0; i < coreopts->size; i++)
- coreopts->opts[i].index = coreopts->opts[i].default_index;
- coreopts->updated = true;
- #ifdef HAVE_CHEEVOS
- rcheevos_validate_config_settings();
- #endif
- runloop_msg_queue_push(
- msg_hash_to_str(MSG_CORE_OPTIONS_RESET),
- 1, 100, true,
- NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO);
- }
- void menu_content_environment_get(int *argc, char *argv[],
- void *args, void *params_data)
- {
- struct rarch_state *p_rarch = &rarch_st;
- struct rarch_main_wrap *wrap_args = (struct rarch_main_wrap*)params_data;
- rarch_system_info_t *sys_info = &p_rarch->runloop_system;
- if (!wrap_args)
- return;
- wrap_args->no_content = sys_info->load_no_content;
- if (!retroarch_override_setting_is_set(RARCH_OVERRIDE_SETTING_VERBOSITY, NULL))
- wrap_args->verbose = verbosity_is_enabled();
- wrap_args->touched = true;
- wrap_args->config_path = NULL;
- wrap_args->sram_path = NULL;
- wrap_args->state_path = NULL;
- wrap_args->content_path = NULL;
- if (!path_is_empty(RARCH_PATH_CONFIG))
- wrap_args->config_path = path_get(RARCH_PATH_CONFIG);
- if (!string_is_empty(p_rarch->dir_savefile))
- wrap_args->sram_path = p_rarch->dir_savefile;
- if (!string_is_empty(p_rarch->dir_savestate))
- wrap_args->state_path = p_rarch->dir_savestate;
- if (!path_is_empty(RARCH_PATH_CONTENT))
- wrap_args->content_path = path_get(RARCH_PATH_CONTENT);
- if (!retroarch_override_setting_is_set(RARCH_OVERRIDE_SETTING_LIBRETRO, NULL))
- wrap_args->libretro_path = string_is_empty(path_get(RARCH_PATH_CORE)) ? NULL :
- path_get(RARCH_PATH_CORE);
- }
- frontend_ctx_driver_t *frontend_get_ptr(void)
- {
- struct rarch_state *p_rarch = &rarch_st;
- return p_rarch->current_frontend_ctx;
- }
- int frontend_driver_parse_drive_list(void *data, bool load_content)
- {
- struct rarch_state *p_rarch = &rarch_st;
- frontend_ctx_driver_t *frontend = p_rarch->current_frontend_ctx;
- if (!frontend || !frontend->parse_drive_list)
- return -1;
- return frontend->parse_drive_list(data, load_content);
- }
- void frontend_driver_content_loaded(void)
- {
- struct rarch_state *p_rarch = &rarch_st;
- frontend_ctx_driver_t *frontend = p_rarch->current_frontend_ctx;
- if (!frontend || !frontend->content_loaded)
- return;
- frontend->content_loaded();
- }
- bool frontend_driver_has_fork(void)
- {
- struct rarch_state *p_rarch = &rarch_st;
- frontend_ctx_driver_t *frontend = p_rarch->current_frontend_ctx;
- if (!frontend || !frontend->set_fork)
- return false;
- return true;
- }
- bool frontend_driver_set_fork(enum frontend_fork fork_mode)
- {
- struct rarch_state *p_rarch = &rarch_st;
- frontend_ctx_driver_t *frontend = p_rarch->current_frontend_ctx;
- if (!frontend_driver_has_fork())
- return false;
- return frontend->set_fork(fork_mode);
- }
- void frontend_driver_process_args(int *argc, char *argv[])
- {
- struct rarch_state *p_rarch = &rarch_st;
- frontend_ctx_driver_t *frontend = p_rarch->current_frontend_ctx;
- if (frontend && frontend->process_args)
- frontend->process_args(argc, argv);
- }
- bool frontend_driver_is_inited(void)
- {
- struct rarch_state *p_rarch = &rarch_st;
- frontend_ctx_driver_t *frontend = p_rarch->current_frontend_ctx;
- if (!frontend)
- return false;
- return true;
- }
- void frontend_driver_init_first(void *args)
- {
- struct rarch_state *p_rarch = &rarch_st;
- p_rarch->current_frontend_ctx = (frontend_ctx_driver_t*)
- frontend_ctx_init_first();
- if (p_rarch->current_frontend_ctx && p_rarch->current_frontend_ctx->init)
- p_rarch->current_frontend_ctx->init(args);
- }
- void frontend_driver_free(void)
- {
- struct rarch_state *p_rarch = &rarch_st;
- if (p_rarch)
- p_rarch->current_frontend_ctx = NULL;
- }
- environment_get_t frontend_driver_environment_get_ptr(void)
- {
- struct rarch_state *p_rarch = &rarch_st;
- frontend_ctx_driver_t *frontend = p_rarch->current_frontend_ctx;
- if (frontend)
- return frontend->environment_get;
- return NULL;
- }
- bool frontend_driver_has_get_video_driver_func(void)
- {
- struct rarch_state *p_rarch = &rarch_st;
- frontend_ctx_driver_t *frontend = p_rarch->current_frontend_ctx;
- if (!frontend || !frontend->get_video_driver)
- return false;
- return true;
- }
- const struct video_driver *frontend_driver_get_video_driver(void)
- {
- struct rarch_state *p_rarch = &rarch_st;
- frontend_ctx_driver_t *frontend = p_rarch->current_frontend_ctx;
- if (!frontend || !frontend->get_video_driver)
- return NULL;
- return frontend->get_video_driver();
- }
- void frontend_driver_exitspawn(char *s, size_t len, char *args)
- {
- struct rarch_state *p_rarch = &rarch_st;
- frontend_ctx_driver_t *frontend = p_rarch->current_frontend_ctx;
- if (frontend && frontend->exitspawn)
- frontend->exitspawn(s, len, args);
- }
- void frontend_driver_deinit(void *args)
- {
- struct rarch_state *p_rarch = &rarch_st;
- frontend_ctx_driver_t *frontend = p_rarch->current_frontend_ctx;
- if (frontend && frontend->deinit)
- frontend->deinit(args);
- }
- void frontend_driver_shutdown(bool a)
- {
- struct rarch_state *p_rarch = &rarch_st;
- frontend_ctx_driver_t *frontend = p_rarch->current_frontend_ctx;
- if (frontend && frontend->shutdown)
- frontend->shutdown(a);
- }
- enum frontend_architecture frontend_driver_get_cpu_architecture(void)
- {
- struct rarch_state *p_rarch = &rarch_st;
- frontend_ctx_driver_t *frontend = p_rarch->current_frontend_ctx;
- if (!frontend || !frontend->get_architecture)
- return FRONTEND_ARCH_NONE;
- return frontend->get_architecture();
- }
- const void *frontend_driver_get_cpu_architecture_str(
- char *architecture, size_t size)
- {
- struct rarch_state *p_rarch = &rarch_st;
- frontend_ctx_driver_t *frontend = p_rarch->current_frontend_ctx;
- enum frontend_architecture arch = frontend_driver_get_cpu_architecture();
- switch (arch)
- {
- case FRONTEND_ARCH_X86:
- strcpy_literal(architecture, "x86");
- break;
- case FRONTEND_ARCH_X86_64:
- strcpy_literal(architecture, "x64");
- break;
- case FRONTEND_ARCH_PPC:
- strcpy_literal(architecture, "PPC");
- break;
- case FRONTEND_ARCH_ARM:
- strcpy_literal(architecture, "ARM");
- break;
- case FRONTEND_ARCH_ARMV7:
- strcpy_literal(architecture, "ARMv7");
- break;
- case FRONTEND_ARCH_ARMV8:
- strcpy_literal(architecture, "ARMv8");
- break;
- case FRONTEND_ARCH_MIPS:
- strcpy_literal(architecture, "MIPS");
- break;
- case FRONTEND_ARCH_TILE:
- strcpy_literal(architecture, "Tilera");
- break;
- case FRONTEND_ARCH_NONE:
- default:
- strcpy_literal(architecture, "N/A");
- break;
- }
- return frontend;
- }
- uint64_t frontend_driver_get_total_memory(void)
- {
- struct rarch_state *p_rarch = &rarch_st;
- frontend_ctx_driver_t *frontend = p_rarch->current_frontend_ctx;
- if (!frontend || !frontend->get_total_mem)
- return 0;
- return frontend->get_total_mem();
- }
- uint64_t frontend_driver_get_free_memory(void)
- {
- struct rarch_state *p_rarch = &rarch_st;
- frontend_ctx_driver_t *frontend = p_rarch->current_frontend_ctx;
- if (!frontend || !frontend->get_free_mem)
- return 0;
- return frontend->get_free_mem();
- }
- void frontend_driver_install_signal_handler(void)
- {
- struct rarch_state *p_rarch = &rarch_st;
- frontend_ctx_driver_t *frontend = p_rarch->current_frontend_ctx;
- if (frontend && frontend->install_signal_handler)
- frontend->install_signal_handler();
- }
- int frontend_driver_get_signal_handler_state(void)
- {
- struct rarch_state *p_rarch = &rarch_st;
- frontend_ctx_driver_t *frontend = p_rarch->current_frontend_ctx;
- if (!frontend || !frontend->get_signal_handler_state)
- return -1;
- return frontend->get_signal_handler_state();
- }
- void frontend_driver_set_signal_handler_state(int value)
- {
- struct rarch_state *p_rarch = &rarch_st;
- frontend_ctx_driver_t *frontend = p_rarch->current_frontend_ctx;
- if (frontend && frontend->set_signal_handler_state)
- frontend->set_signal_handler_state(value);
- }
- void frontend_driver_attach_console(void)
- {
- struct rarch_state *p_rarch = &rarch_st;
- frontend_ctx_driver_t *frontend = p_rarch->current_frontend_ctx;
- if (frontend && frontend->attach_console)
- frontend->attach_console();
- }
- void frontend_driver_set_screen_brightness(int value)
- {
- struct rarch_state *p_rarch = &rarch_st;
- frontend_ctx_driver_t *frontend = p_rarch->current_frontend_ctx;
- if (frontend && frontend->set_screen_brightness)
- frontend->set_screen_brightness(value);
- }
- bool frontend_driver_can_set_screen_brightness()
- {
- struct rarch_state *p_rarch = &rarch_st;
- frontend_ctx_driver_t *frontend = p_rarch->current_frontend_ctx;
- return (frontend && frontend->set_screen_brightness);
- }
- void frontend_driver_detach_console(void)
- {
- struct rarch_state *p_rarch = &rarch_st;
- frontend_ctx_driver_t *frontend = p_rarch->current_frontend_ctx;
- if (frontend && frontend->detach_console)
- frontend->detach_console();
- }
- void frontend_driver_destroy_signal_handler_state(void)
- {
- struct rarch_state *p_rarch = &rarch_st;
- frontend_ctx_driver_t *frontend = p_rarch->current_frontend_ctx;
- if (frontend && frontend->destroy_signal_handler_state)
- frontend->destroy_signal_handler_state();
- }
- bool frontend_driver_can_watch_for_changes(void)
- {
- struct rarch_state *p_rarch = &rarch_st;
- frontend_ctx_driver_t *frontend = p_rarch->current_frontend_ctx;
- if (!frontend || !frontend->watch_path_for_changes)
- return false;
- return true;
- }
- void frontend_driver_watch_path_for_changes(
- struct string_list *list, int flags,
- path_change_data_t **change_data)
- {
- struct rarch_state *p_rarch = &rarch_st;
- frontend_ctx_driver_t *frontend = p_rarch->current_frontend_ctx;
- if (frontend && frontend->watch_path_for_changes)
- frontend->watch_path_for_changes(list, flags, change_data);
- }
- bool frontend_driver_check_for_path_changes(path_change_data_t *change_data)
- {
- struct rarch_state *p_rarch = &rarch_st;
- frontend_ctx_driver_t *frontend = p_rarch->current_frontend_ctx;
- if (!frontend || !frontend->check_for_path_changes)
- return false;
- return frontend->check_for_path_changes(change_data);
- }
- void frontend_driver_set_sustained_performance_mode(bool on)
- {
- struct rarch_state *p_rarch = &rarch_st;
- frontend_ctx_driver_t *frontend = p_rarch->current_frontend_ctx;
- if (frontend && frontend->set_sustained_performance_mode)
- frontend->set_sustained_performance_mode(on);
- }
- const char* frontend_driver_get_cpu_model_name(void)
- {
- struct rarch_state *p_rarch = &rarch_st;
- frontend_ctx_driver_t *frontend = p_rarch->current_frontend_ctx;
- if (!frontend || !frontend->get_cpu_model_name)
- return NULL;
- return frontend->get_cpu_model_name();
- }
- enum retro_language frontend_driver_get_user_language(void)
- {
- struct rarch_state *p_rarch = &rarch_st;
- frontend_ctx_driver_t *frontend = p_rarch->current_frontend_ctx;
- if (!frontend || !frontend->get_user_language)
- return RETRO_LANGUAGE_ENGLISH;
- return frontend->get_user_language();
- }
|