Audio plugin host https://kx.studio/carla
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

juce_win32_Windowing.cpp 169KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676367736783679368036813682368336843685368636873688368936903691369236933694369536963697369836993700370137023703370437053706370737083709371037113712371337143715371637173718371937203721372237233724372537263727372837293730373137323733373437353736373737383739374037413742374337443745374637473748374937503751375237533754375537563757375837593760376137623763376437653766376737683769377037713772377337743775377637773778377937803781378237833784378537863787378837893790379137923793379437953796379737983799380038013802380338043805380638073808380938103811381238133814381538163817381838193820382138223823382438253826382738283829383038313832383338343835383638373838383938403841384238433844384538463847384838493850385138523853385438553856385738583859386038613862386338643865386638673868386938703871387238733874387538763877387838793880388138823883388438853886388738883889389038913892389338943895389638973898389939003901390239033904390539063907390839093910391139123913391439153916391739183919392039213922392339243925392639273928392939303931393239333934393539363937393839393940394139423943394439453946394739483949395039513952395339543955395639573958395939603961396239633964396539663967396839693970397139723973397439753976397739783979398039813982398339843985398639873988398939903991399239933994399539963997399839994000400140024003400440054006400740084009401040114012401340144015401640174018401940204021402240234024402540264027402840294030403140324033403440354036403740384039404040414042404340444045404640474048404940504051405240534054405540564057405840594060406140624063406440654066406740684069407040714072407340744075407640774078407940804081408240834084408540864087408840894090409140924093409440954096409740984099410041014102410341044105410641074108410941104111411241134114411541164117411841194120412141224123412441254126412741284129413041314132413341344135413641374138413941404141414241434144414541464147414841494150415141524153415441554156415741584159416041614162416341644165416641674168416941704171417241734174417541764177417841794180418141824183418441854186418741884189419041914192419341944195419641974198419942004201420242034204420542064207420842094210421142124213421442154216421742184219422042214222422342244225422642274228422942304231423242334234423542364237423842394240424142424243424442454246424742484249425042514252425342544255425642574258425942604261426242634264426542664267426842694270427142724273427442754276427742784279428042814282428342844285428642874288428942904291429242934294429542964297429842994300430143024303430443054306430743084309431043114312431343144315431643174318431943204321432243234324432543264327432843294330433143324333433443354336433743384339434043414342434343444345434643474348434943504351435243534354435543564357435843594360436143624363436443654366436743684369437043714372437343744375437643774378437943804381438243834384438543864387438843894390439143924393439443954396439743984399440044014402440344044405440644074408440944104411441244134414441544164417441844194420442144224423442444254426442744284429443044314432443344344435443644374438443944404441444244434444444544464447444844494450445144524453445444554456445744584459446044614462446344644465446644674468446944704471447244734474447544764477447844794480448144824483448444854486448744884489449044914492449344944495449644974498449945004501450245034504450545064507450845094510451145124513451445154516451745184519452045214522452345244525452645274528452945304531453245334534453545364537453845394540454145424543454445454546454745484549455045514552455345544555455645574558455945604561456245634564456545664567456845694570457145724573457445754576457745784579458045814582458345844585458645874588458945904591459245934594459545964597459845994600460146024603460446054606460746084609461046114612461346144615461646174618461946204621462246234624462546264627462846294630463146324633463446354636463746384639464046414642464346444645464646474648464946504651465246534654465546564657465846594660466146624663466446654666466746684669467046714672467346744675467646774678467946804681468246834684468546864687468846894690469146924693469446954696469746984699470047014702470347044705470647074708470947104711471247134714471547164717471847194720472147224723472447254726472747284729473047314732473347344735473647374738473947404741474247434744474547464747474847494750
  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE 6 technical preview.
  4. Copyright (c) 2020 - Raw Material Software Limited
  5. You may use this code under the terms of the GPL v3
  6. (see www.gnu.org/licenses).
  7. For this technical preview, this file is not subject to commercial licensing.
  8. JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
  9. EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
  10. DISCLAIMED.
  11. ==============================================================================
  12. */
  13. #if JUCE_MODULE_AVAILABLE_juce_audio_plugin_client
  14. #include <juce_audio_plugin_client/AAX/juce_AAX_Modifier_Injector.h>
  15. #endif
  16. #if JUCE_WIN_PER_MONITOR_DPI_AWARE && JUCE_MODULE_AVAILABLE_juce_gui_extra
  17. #include <juce_gui_extra/embedding/juce_ScopedDPIAwarenessDisabler.h>
  18. #endif
  19. namespace juce
  20. {
  21. JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wcast-function-type")
  22. #undef GetSystemMetrics // multimon overrides this for some reason and causes a mess..
  23. // these are in the windows SDK, but need to be repeated here for GCC..
  24. #ifndef GET_APPCOMMAND_LPARAM
  25. #define GET_APPCOMMAND_LPARAM(lParam) ((short) (HIWORD (lParam) & ~FAPPCOMMAND_MASK))
  26. #define FAPPCOMMAND_MASK 0xF000
  27. #define APPCOMMAND_MEDIA_NEXTTRACK 11
  28. #define APPCOMMAND_MEDIA_PREVIOUSTRACK 12
  29. #define APPCOMMAND_MEDIA_STOP 13
  30. #define APPCOMMAND_MEDIA_PLAY_PAUSE 14
  31. #endif
  32. #ifndef WM_APPCOMMAND
  33. #define WM_APPCOMMAND 0x0319
  34. #endif
  35. void juce_repeatLastProcessPriority();
  36. bool juce_isRunningInWine();
  37. using CheckEventBlockedByModalComps = bool (*) (const MSG&);
  38. extern CheckEventBlockedByModalComps isEventBlockedByModalComps;
  39. static bool shouldDeactivateTitleBar = true;
  40. void* getUser32Function (const char*);
  41. //==============================================================================
  42. #ifndef WM_TOUCH
  43. enum
  44. {
  45. WM_TOUCH = 0x0240,
  46. TOUCHEVENTF_MOVE = 0x0001,
  47. TOUCHEVENTF_DOWN = 0x0002,
  48. TOUCHEVENTF_UP = 0x0004
  49. };
  50. typedef HANDLE HTOUCHINPUT;
  51. typedef HANDLE HGESTUREINFO;
  52. struct TOUCHINPUT
  53. {
  54. LONG x;
  55. LONG y;
  56. HANDLE hSource;
  57. DWORD dwID;
  58. DWORD dwFlags;
  59. DWORD dwMask;
  60. DWORD dwTime;
  61. ULONG_PTR dwExtraInfo;
  62. DWORD cxContact;
  63. DWORD cyContact;
  64. };
  65. struct GESTUREINFO
  66. {
  67. UINT cbSize;
  68. DWORD dwFlags;
  69. DWORD dwID;
  70. HWND hwndTarget;
  71. POINTS ptsLocation;
  72. DWORD dwInstanceID;
  73. DWORD dwSequenceID;
  74. ULONGLONG ullArguments;
  75. UINT cbExtraArgs;
  76. };
  77. #endif
  78. #ifndef WM_NCPOINTERUPDATE
  79. enum
  80. {
  81. WM_NCPOINTERUPDATE = 0x241,
  82. WM_NCPOINTERDOWN = 0x242,
  83. WM_NCPOINTERUP = 0x243,
  84. WM_POINTERUPDATE = 0x245,
  85. WM_POINTERDOWN = 0x246,
  86. WM_POINTERUP = 0x247,
  87. WM_POINTERENTER = 0x249,
  88. WM_POINTERLEAVE = 0x24A,
  89. WM_POINTERACTIVATE = 0x24B,
  90. WM_POINTERCAPTURECHANGED = 0x24C,
  91. WM_TOUCHHITTESTING = 0x24D,
  92. WM_POINTERWHEEL = 0x24E,
  93. WM_POINTERHWHEEL = 0x24F,
  94. WM_POINTERHITTEST = 0x250
  95. };
  96. enum
  97. {
  98. PT_TOUCH = 0x00000002,
  99. PT_PEN = 0x00000003
  100. };
  101. enum POINTER_BUTTON_CHANGE_TYPE
  102. {
  103. POINTER_CHANGE_NONE,
  104. POINTER_CHANGE_FIRSTBUTTON_DOWN,
  105. POINTER_CHANGE_FIRSTBUTTON_UP,
  106. POINTER_CHANGE_SECONDBUTTON_DOWN,
  107. POINTER_CHANGE_SECONDBUTTON_UP,
  108. POINTER_CHANGE_THIRDBUTTON_DOWN,
  109. POINTER_CHANGE_THIRDBUTTON_UP,
  110. POINTER_CHANGE_FOURTHBUTTON_DOWN,
  111. POINTER_CHANGE_FOURTHBUTTON_UP,
  112. POINTER_CHANGE_FIFTHBUTTON_DOWN,
  113. POINTER_CHANGE_FIFTHBUTTON_UP
  114. };
  115. enum
  116. {
  117. PEN_MASK_NONE = 0x00000000,
  118. PEN_MASK_PRESSURE = 0x00000001,
  119. PEN_MASK_ROTATION = 0x00000002,
  120. PEN_MASK_TILT_X = 0x00000004,
  121. PEN_MASK_TILT_Y = 0x00000008
  122. };
  123. enum
  124. {
  125. TOUCH_MASK_NONE = 0x00000000,
  126. TOUCH_MASK_CONTACTAREA = 0x00000001,
  127. TOUCH_MASK_ORIENTATION = 0x00000002,
  128. TOUCH_MASK_PRESSURE = 0x00000004
  129. };
  130. enum
  131. {
  132. POINTER_FLAG_NONE = 0x00000000,
  133. POINTER_FLAG_NEW = 0x00000001,
  134. POINTER_FLAG_INRANGE = 0x00000002,
  135. POINTER_FLAG_INCONTACT = 0x00000004,
  136. POINTER_FLAG_FIRSTBUTTON = 0x00000010,
  137. POINTER_FLAG_SECONDBUTTON = 0x00000020,
  138. POINTER_FLAG_THIRDBUTTON = 0x00000040,
  139. POINTER_FLAG_FOURTHBUTTON = 0x00000080,
  140. POINTER_FLAG_FIFTHBUTTON = 0x00000100,
  141. POINTER_FLAG_PRIMARY = 0x00002000,
  142. POINTER_FLAG_CONFIDENCE = 0x00004000,
  143. POINTER_FLAG_CANCELED = 0x00008000,
  144. POINTER_FLAG_DOWN = 0x00010000,
  145. POINTER_FLAG_UPDATE = 0x00020000,
  146. POINTER_FLAG_UP = 0x00040000,
  147. POINTER_FLAG_WHEEL = 0x00080000,
  148. POINTER_FLAG_HWHEEL = 0x00100000,
  149. POINTER_FLAG_CAPTURECHANGED = 0x00200000,
  150. POINTER_FLAG_HASTRANSFORM = 0x00400000
  151. };
  152. typedef DWORD POINTER_INPUT_TYPE;
  153. typedef UINT32 POINTER_FLAGS;
  154. typedef UINT32 PEN_FLAGS;
  155. typedef UINT32 PEN_MASK;
  156. typedef UINT32 TOUCH_FLAGS;
  157. typedef UINT32 TOUCH_MASK;
  158. struct POINTER_INFO
  159. {
  160. POINTER_INPUT_TYPE pointerType;
  161. UINT32 pointerId;
  162. UINT32 frameId;
  163. POINTER_FLAGS pointerFlags;
  164. HANDLE sourceDevice;
  165. HWND hwndTarget;
  166. POINT ptPixelLocation;
  167. POINT ptHimetricLocation;
  168. POINT ptPixelLocationRaw;
  169. POINT ptHimetricLocationRaw;
  170. DWORD dwTime;
  171. UINT32 historyCount;
  172. INT32 InputData;
  173. DWORD dwKeyStates;
  174. UINT64 PerformanceCount;
  175. POINTER_BUTTON_CHANGE_TYPE ButtonChangeType;
  176. };
  177. struct POINTER_TOUCH_INFO
  178. {
  179. POINTER_INFO pointerInfo;
  180. TOUCH_FLAGS touchFlags;
  181. TOUCH_MASK touchMask;
  182. RECT rcContact;
  183. RECT rcContactRaw;
  184. UINT32 orientation;
  185. UINT32 pressure;
  186. };
  187. struct POINTER_PEN_INFO
  188. {
  189. POINTER_INFO pointerInfo;
  190. PEN_FLAGS penFlags;
  191. PEN_MASK penMask;
  192. UINT32 pressure;
  193. UINT32 rotation;
  194. INT32 tiltX;
  195. INT32 tiltY;
  196. };
  197. #define GET_POINTERID_WPARAM(wParam) (LOWORD(wParam))
  198. #endif
  199. #ifndef MONITOR_DPI_TYPE
  200. enum Monitor_DPI_Type
  201. {
  202. MDT_Effective_DPI = 0,
  203. MDT_Angular_DPI = 1,
  204. MDT_Raw_DPI = 2,
  205. MDT_Default = MDT_Effective_DPI
  206. };
  207. #endif
  208. #ifndef DPI_AWARENESS
  209. enum DPI_Awareness
  210. {
  211. DPI_Awareness_Invalid = -1,
  212. DPI_Awareness_Unaware = 0,
  213. DPI_Awareness_System_Aware = 1,
  214. DPI_Awareness_Per_Monitor_Aware = 2
  215. };
  216. #endif
  217. #ifndef USER_DEFAULT_SCREEN_DPI
  218. #define USER_DEFAULT_SCREEN_DPI 96
  219. #endif
  220. #ifndef _DPI_AWARENESS_CONTEXTS_
  221. typedef HANDLE DPI_AWARENESS_CONTEXT;
  222. #define DPI_AWARENESS_CONTEXT_UNAWARE ((DPI_AWARENESS_CONTEXT) - 1)
  223. #define DPI_AWARENESS_CONTEXT_SYSTEM_AWARE ((DPI_AWARENESS_CONTEXT) - 2)
  224. #define DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE ((DPI_AWARENESS_CONTEXT) - 3)
  225. #define DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2 ((DPI_AWARENESS_CONTEXT) - 4)
  226. #endif
  227. // Some versions of the Windows 10 SDK define _DPI_AWARENESS_CONTEXTS_ but not
  228. // DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2
  229. #ifndef DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2
  230. #define DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2 ((DPI_AWARENESS_CONTEXT) - 4)
  231. #endif
  232. //==============================================================================
  233. using RegisterTouchWindowFunc = BOOL (WINAPI*) (HWND, ULONG);
  234. using GetTouchInputInfoFunc = BOOL (WINAPI*) (HTOUCHINPUT, UINT, TOUCHINPUT*, int);
  235. using CloseTouchInputHandleFunc = BOOL (WINAPI*) (HTOUCHINPUT);
  236. using GetGestureInfoFunc = BOOL (WINAPI*) (HGESTUREINFO, GESTUREINFO*);
  237. static RegisterTouchWindowFunc registerTouchWindow = nullptr;
  238. static GetTouchInputInfoFunc getTouchInputInfo = nullptr;
  239. static CloseTouchInputHandleFunc closeTouchInputHandle = nullptr;
  240. static GetGestureInfoFunc getGestureInfo = nullptr;
  241. static bool hasCheckedForMultiTouch = false;
  242. static bool canUseMultiTouch()
  243. {
  244. if (registerTouchWindow == nullptr && ! hasCheckedForMultiTouch)
  245. {
  246. hasCheckedForMultiTouch = true;
  247. registerTouchWindow = (RegisterTouchWindowFunc) getUser32Function ("RegisterTouchWindow");
  248. getTouchInputInfo = (GetTouchInputInfoFunc) getUser32Function ("GetTouchInputInfo");
  249. closeTouchInputHandle = (CloseTouchInputHandleFunc) getUser32Function ("CloseTouchInputHandle");
  250. getGestureInfo = (GetGestureInfoFunc) getUser32Function ("GetGestureInfo");
  251. }
  252. return registerTouchWindow != nullptr;
  253. }
  254. //==============================================================================
  255. using GetPointerTypeFunc = BOOL (WINAPI*) (UINT32, POINTER_INPUT_TYPE*);
  256. using GetPointerTouchInfoFunc = BOOL (WINAPI*) (UINT32, POINTER_TOUCH_INFO*);
  257. using GetPointerPenInfoFunc = BOOL (WINAPI*) (UINT32, POINTER_PEN_INFO*);
  258. static GetPointerTypeFunc getPointerTypeFunction = nullptr;
  259. static GetPointerTouchInfoFunc getPointerTouchInfo = nullptr;
  260. static GetPointerPenInfoFunc getPointerPenInfo = nullptr;
  261. static bool canUsePointerAPI = false;
  262. static void checkForPointerAPI()
  263. {
  264. getPointerTypeFunction = (GetPointerTypeFunc) getUser32Function ("GetPointerType");
  265. getPointerTouchInfo = (GetPointerTouchInfoFunc) getUser32Function ("GetPointerTouchInfo");
  266. getPointerPenInfo = (GetPointerPenInfoFunc) getUser32Function ("GetPointerPenInfo");
  267. canUsePointerAPI = (getPointerTypeFunction != nullptr
  268. && getPointerTouchInfo != nullptr
  269. && getPointerPenInfo != nullptr);
  270. }
  271. //==============================================================================
  272. using SetProcessDPIAwareFunc = BOOL (WINAPI*) ();
  273. using SetProcessDPIAwarenessContextFunc = BOOL (WINAPI*) (DPI_AWARENESS_CONTEXT);
  274. using SetProcessDPIAwarenessFunc = BOOL (WINAPI*) (DPI_Awareness);
  275. using SetThreadDPIAwarenessContextFunc = DPI_AWARENESS_CONTEXT (WINAPI*) (DPI_AWARENESS_CONTEXT);
  276. using GetDPIForWindowFunc = UINT (WINAPI*) (HWND);
  277. using GetDPIForMonitorFunc = HRESULT (WINAPI*) (HMONITOR, Monitor_DPI_Type, UINT*, UINT*);
  278. using GetProcessDPIAwarenessFunc = HRESULT (WINAPI*) (HANDLE, DPI_Awareness*);
  279. using GetWindowDPIAwarenessContextFunc = DPI_AWARENESS_CONTEXT (WINAPI*) (HWND);
  280. using GetThreadDPIAwarenessContextFunc = DPI_AWARENESS_CONTEXT (WINAPI*) ();
  281. using GetAwarenessFromDpiAwarenessContextFunc = DPI_Awareness (WINAPI*) (DPI_AWARENESS_CONTEXT);
  282. using EnableNonClientDPIScalingFunc = BOOL (WINAPI*) (HWND);
  283. static SetProcessDPIAwareFunc setProcessDPIAware = nullptr;
  284. static SetProcessDPIAwarenessContextFunc setProcessDPIAwarenessContext = nullptr;
  285. static SetProcessDPIAwarenessFunc setProcessDPIAwareness = nullptr;
  286. static SetThreadDPIAwarenessContextFunc setThreadDPIAwarenessContext = nullptr;
  287. static GetDPIForMonitorFunc getDPIForMonitor = nullptr;
  288. static GetDPIForWindowFunc getDPIForWindow = nullptr;
  289. static GetProcessDPIAwarenessFunc getProcessDPIAwareness = nullptr;
  290. static GetWindowDPIAwarenessContextFunc getWindowDPIAwarenessContext = nullptr;
  291. static GetThreadDPIAwarenessContextFunc getThreadDPIAwarenessContext = nullptr;
  292. static GetAwarenessFromDpiAwarenessContextFunc getAwarenessFromDPIAwarenessContext = nullptr;
  293. static EnableNonClientDPIScalingFunc enableNonClientDPIScaling = nullptr;
  294. static bool hasCheckedForDPIAwareness = false;
  295. static void setDPIAwareness()
  296. {
  297. if (hasCheckedForDPIAwareness)
  298. return;
  299. hasCheckedForDPIAwareness = true;
  300. if (! JUCEApplicationBase::isStandaloneApp())
  301. return;
  302. HMODULE shcoreModule = GetModuleHandleA ("SHCore.dll");
  303. if (shcoreModule != nullptr)
  304. {
  305. getDPIForMonitor = (GetDPIForMonitorFunc) GetProcAddress (shcoreModule, "GetDpiForMonitor");
  306. #if JUCE_WIN_PER_MONITOR_DPI_AWARE
  307. getDPIForWindow = (GetDPIForWindowFunc) getUser32Function ("GetDpiForWindow");
  308. getProcessDPIAwareness = (GetProcessDPIAwarenessFunc) GetProcAddress (shcoreModule, "GetProcessDpiAwareness");
  309. getWindowDPIAwarenessContext = (GetWindowDPIAwarenessContextFunc) getUser32Function ("GetWindowDpiAwarenessContext");
  310. setThreadDPIAwarenessContext = (SetThreadDPIAwarenessContextFunc) getUser32Function ("SetThreadDpiAwarenessContext");
  311. getThreadDPIAwarenessContext = (GetThreadDPIAwarenessContextFunc) getUser32Function ("GetThreadDpiAwarenessContext");
  312. getAwarenessFromDPIAwarenessContext = (GetAwarenessFromDpiAwarenessContextFunc) getUser32Function ("GetAwarenessFromDpiAwarenessContext");
  313. setProcessDPIAwareness = (SetProcessDPIAwarenessFunc) GetProcAddress (shcoreModule, "SetProcessDpiAwareness");
  314. setProcessDPIAwarenessContext = (SetProcessDPIAwarenessContextFunc) getUser32Function ("SetProcessDpiAwarenessContext");
  315. if (setProcessDPIAwarenessContext != nullptr
  316. && SUCCEEDED (setProcessDPIAwarenessContext (DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2)))
  317. return;
  318. enableNonClientDPIScaling = (EnableNonClientDPIScalingFunc) getUser32Function ("EnableNonClientDpiScaling");
  319. if (setProcessDPIAwareness != nullptr && enableNonClientDPIScaling != nullptr
  320. && SUCCEEDED (setProcessDPIAwareness (DPI_Awareness::DPI_Awareness_Per_Monitor_Aware)))
  321. return;
  322. #endif
  323. if (setProcessDPIAwareness == nullptr)
  324. setProcessDPIAwareness = (SetProcessDPIAwarenessFunc) GetProcAddress (shcoreModule, "SetProcessDpiAwareness");
  325. if (setProcessDPIAwareness != nullptr && getDPIForMonitor != nullptr
  326. && SUCCEEDED (setProcessDPIAwareness (DPI_Awareness::DPI_Awareness_System_Aware)))
  327. return;
  328. }
  329. // fallback for pre Windows 8.1 - equivalent to Process_System_DPI_Aware
  330. setProcessDPIAware = (SetProcessDPIAwareFunc) getUser32Function ("SetProcessDPIAware");
  331. if (setProcessDPIAware != nullptr)
  332. setProcessDPIAware();
  333. }
  334. static bool isPerMonitorDPIAwareProcess()
  335. {
  336. #if JUCE_WIN_PER_MONITOR_DPI_AWARE
  337. static bool dpiAware = []() -> bool
  338. {
  339. setDPIAwareness();
  340. if (getProcessDPIAwareness == nullptr)
  341. return false;
  342. DPI_Awareness context;
  343. getProcessDPIAwareness (nullptr, &context);
  344. return context == DPI_Awareness::DPI_Awareness_Per_Monitor_Aware;
  345. }();
  346. return dpiAware;
  347. #else
  348. return false;
  349. #endif
  350. }
  351. static bool isPerMonitorDPIAwareWindow (HWND h)
  352. {
  353. #if JUCE_WIN_PER_MONITOR_DPI_AWARE
  354. jassert (h != nullptr);
  355. setDPIAwareness();
  356. if (getWindowDPIAwarenessContext != nullptr && getAwarenessFromDPIAwarenessContext != nullptr)
  357. return getAwarenessFromDPIAwarenessContext (getWindowDPIAwarenessContext (h)) == DPI_Awareness::DPI_Awareness_Per_Monitor_Aware;
  358. return isPerMonitorDPIAwareProcess();
  359. #else
  360. ignoreUnused (h);
  361. return false;
  362. #endif
  363. }
  364. static bool isPerMonitorDPIAwareThread()
  365. {
  366. #if JUCE_WIN_PER_MONITOR_DPI_AWARE
  367. setDPIAwareness();
  368. if (getThreadDPIAwarenessContext != nullptr && getAwarenessFromDPIAwarenessContext != nullptr)
  369. return getAwarenessFromDPIAwarenessContext (getThreadDPIAwarenessContext()) == DPI_Awareness::DPI_Awareness_Per_Monitor_Aware;
  370. return isPerMonitorDPIAwareProcess();
  371. #else
  372. return false;
  373. #endif
  374. }
  375. static double getGlobalDPI()
  376. {
  377. setDPIAwareness();
  378. HDC dc = GetDC (nullptr);
  379. auto dpi = (GetDeviceCaps (dc, LOGPIXELSX) + GetDeviceCaps (dc, LOGPIXELSY)) / 2.0;
  380. ReleaseDC (nullptr, dc);
  381. return dpi;
  382. }
  383. //==============================================================================
  384. #if JUCE_WIN_PER_MONITOR_DPI_AWARE && JUCE_MODULE_AVAILABLE_juce_gui_extra
  385. ScopedDPIAwarenessDisabler::ScopedDPIAwarenessDisabler()
  386. {
  387. if (! isPerMonitorDPIAwareThread())
  388. return;
  389. if (setThreadDPIAwarenessContext != nullptr)
  390. previousContext = setThreadDPIAwarenessContext (DPI_AWARENESS_CONTEXT_UNAWARE);
  391. }
  392. ScopedDPIAwarenessDisabler::~ScopedDPIAwarenessDisabler()
  393. {
  394. if (previousContext != nullptr)
  395. setThreadDPIAwarenessContext ((DPI_AWARENESS_CONTEXT) previousContext);
  396. }
  397. #endif
  398. //==============================================================================
  399. using SettingChangeCallbackFunc = void (*)(void);
  400. extern SettingChangeCallbackFunc settingChangeCallback;
  401. //==============================================================================
  402. static Rectangle<int> rectangleFromRECT (const RECT& r) noexcept { return { r.left, r.top, r.right - r.left, r.bottom - r.top }; }
  403. static RECT RECTFromRectangle (const Rectangle<int>& r) noexcept { return { r.getX(), r.getY(), r.getRight(), r.getBottom() }; }
  404. static Point<int> pointFromPOINT (const POINT& p) noexcept { return { p.x, p.y }; }
  405. static POINT POINTFromPoint (const Point<int>& p) noexcept { return { p.x, p.y }; }
  406. //==============================================================================
  407. static const Displays::Display* getCurrentDisplayFromScaleFactor (HWND hwnd);
  408. static Rectangle<int> convertPhysicalScreenRectangleToLogical (const Rectangle<int>& r, HWND h) noexcept
  409. {
  410. if (isPerMonitorDPIAwareWindow (h))
  411. return Desktop::getInstance().getDisplays().physicalToLogical (r, getCurrentDisplayFromScaleFactor (h));
  412. return r;
  413. }
  414. static Rectangle<int> convertLogicalScreenRectangleToPhysical (const Rectangle<int>& r, HWND h) noexcept
  415. {
  416. if (isPerMonitorDPIAwareWindow (h))
  417. return Desktop::getInstance().getDisplays().logicalToPhysical (r, getCurrentDisplayFromScaleFactor (h));
  418. return r;
  419. }
  420. static Point<int> convertPhysicalScreenPointToLogical (const Point<int>& p, HWND h) noexcept
  421. {
  422. if (isPerMonitorDPIAwareWindow (h))
  423. return Desktop::getInstance().getDisplays().physicalToLogical (p, getCurrentDisplayFromScaleFactor (h));
  424. return p;
  425. }
  426. JUCE_API double getScaleFactorForWindow (HWND h)
  427. {
  428. // NB. Using a local function here because we need to call this method from the plug-in wrappers
  429. // which don't load the DPI-awareness functions on startup
  430. static GetDPIForWindowFunc localGetDPIForWindow = nullptr;
  431. static bool hasChecked = false;
  432. if (! hasChecked)
  433. {
  434. hasChecked = true;
  435. if (localGetDPIForWindow == nullptr)
  436. localGetDPIForWindow = (GetDPIForWindowFunc) getUser32Function ("GetDpiForWindow");
  437. }
  438. if (localGetDPIForWindow != nullptr)
  439. return (double) localGetDPIForWindow (h) / USER_DEFAULT_SCREEN_DPI;
  440. return 1.0;
  441. }
  442. #if JUCE_WIN_PER_MONITOR_DPI_AWARE
  443. JUCE_API void setThreadDPIAwarenessForWindow (HWND nativeWindow)
  444. {
  445. // NB. Using local functions here because we need to call this method from the plug-in wrappers
  446. // which don't load the DPI-awareness functions on startup
  447. static SetThreadDPIAwarenessContextFunc localSetThreadDPIAwarenessContext = nullptr;
  448. static GetWindowDPIAwarenessContextFunc localGetWindowDPIAwarenessContext = nullptr;
  449. static GetThreadDPIAwarenessContextFunc localGetThreadDPIAwarenessContext = nullptr;
  450. static GetAwarenessFromDpiAwarenessContextFunc localGetAwarenessFromDPIAwarenessContext = nullptr;
  451. static bool hasChecked = false;
  452. static bool loadedOK = false;
  453. if (! hasChecked)
  454. {
  455. hasChecked = true;
  456. localSetThreadDPIAwarenessContext = (SetThreadDPIAwarenessContextFunc) getUser32Function ("SetThreadDpiAwarenessContext");
  457. localGetWindowDPIAwarenessContext = (GetWindowDPIAwarenessContextFunc) getUser32Function ("GetWindowDpiAwarenessContext");
  458. localGetThreadDPIAwarenessContext = (GetThreadDPIAwarenessContextFunc) getUser32Function ("GetThreadDpiAwarenessContext");
  459. localGetAwarenessFromDPIAwarenessContext = (GetAwarenessFromDpiAwarenessContextFunc) getUser32Function ("GetAwarenessFromDpiAwarenessContext");
  460. loadedOK = (localSetThreadDPIAwarenessContext != nullptr && localGetWindowDPIAwarenessContext != nullptr
  461. && localGetThreadDPIAwarenessContext != nullptr && localGetAwarenessFromDPIAwarenessContext != nullptr);
  462. }
  463. if (loadedOK)
  464. {
  465. auto dpiAwareWindow = localGetAwarenessFromDPIAwarenessContext (localGetWindowDPIAwarenessContext (nativeWindow)) == DPI_Awareness::DPI_Awareness_Per_Monitor_Aware;
  466. auto dpiAwareThread = localGetAwarenessFromDPIAwarenessContext (localGetThreadDPIAwarenessContext()) == DPI_Awareness::DPI_Awareness_Per_Monitor_Aware;
  467. if (dpiAwareWindow && ! dpiAwareThread)
  468. localSetThreadDPIAwarenessContext (DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE);
  469. else if (! dpiAwareWindow && dpiAwareThread)
  470. localSetThreadDPIAwarenessContext (DPI_AWARENESS_CONTEXT_UNAWARE);
  471. }
  472. }
  473. #endif
  474. //==============================================================================
  475. static void setWindowPos (HWND hwnd, Rectangle<int> bounds, UINT flags, bool adjustTopLeft = false)
  476. {
  477. if (isPerMonitorDPIAwareWindow (hwnd))
  478. {
  479. if (adjustTopLeft)
  480. bounds = convertLogicalScreenRectangleToPhysical (bounds, hwnd)
  481. .withPosition (Desktop::getInstance().getDisplays().logicalToPhysical (bounds.getTopLeft()));
  482. else
  483. bounds = convertLogicalScreenRectangleToPhysical (bounds, hwnd);
  484. }
  485. SetWindowPos (hwnd, nullptr, bounds.getX(), bounds.getY(), bounds.getWidth(), bounds.getHeight(), flags);
  486. }
  487. static RECT getWindowRect (HWND hwnd)
  488. {
  489. #if JUCE_WIN_PER_MONITOR_DPI_AWARE
  490. setThreadDPIAwarenessForWindow (hwnd);
  491. #endif
  492. RECT r;
  493. GetWindowRect (hwnd, &r);
  494. return r;
  495. }
  496. static void setWindowZOrder (HWND hwnd, HWND insertAfter)
  497. {
  498. SetWindowPos (hwnd, insertAfter, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOSENDCHANGING);
  499. }
  500. //==============================================================================
  501. double Desktop::getDefaultMasterScale()
  502. {
  503. if (! JUCEApplicationBase::isStandaloneApp()
  504. #if JUCE_WIN_PER_MONITOR_DPI_AWARE
  505. || isPerMonitorDPIAwareProcess()
  506. #endif
  507. )
  508. {
  509. return 1.0;
  510. }
  511. return getGlobalDPI() / USER_DEFAULT_SCREEN_DPI;
  512. }
  513. bool Desktop::canUseSemiTransparentWindows() noexcept { return true; }
  514. Desktop::DisplayOrientation Desktop::getCurrentOrientation() const
  515. {
  516. return upright;
  517. }
  518. int64 getMouseEventTime()
  519. {
  520. static int64 eventTimeOffset = 0;
  521. static LONG lastMessageTime = 0;
  522. const LONG thisMessageTime = GetMessageTime();
  523. if (thisMessageTime < lastMessageTime || lastMessageTime == 0)
  524. {
  525. lastMessageTime = thisMessageTime;
  526. eventTimeOffset = Time::currentTimeMillis() - thisMessageTime;
  527. }
  528. return eventTimeOffset + thisMessageTime;
  529. }
  530. //==============================================================================
  531. const int extendedKeyModifier = 0x10000;
  532. const int KeyPress::spaceKey = VK_SPACE;
  533. const int KeyPress::returnKey = VK_RETURN;
  534. const int KeyPress::escapeKey = VK_ESCAPE;
  535. const int KeyPress::backspaceKey = VK_BACK;
  536. const int KeyPress::deleteKey = VK_DELETE | extendedKeyModifier;
  537. const int KeyPress::insertKey = VK_INSERT | extendedKeyModifier;
  538. const int KeyPress::tabKey = VK_TAB;
  539. const int KeyPress::leftKey = VK_LEFT | extendedKeyModifier;
  540. const int KeyPress::rightKey = VK_RIGHT | extendedKeyModifier;
  541. const int KeyPress::upKey = VK_UP | extendedKeyModifier;
  542. const int KeyPress::downKey = VK_DOWN | extendedKeyModifier;
  543. const int KeyPress::homeKey = VK_HOME | extendedKeyModifier;
  544. const int KeyPress::endKey = VK_END | extendedKeyModifier;
  545. const int KeyPress::pageUpKey = VK_PRIOR | extendedKeyModifier;
  546. const int KeyPress::pageDownKey = VK_NEXT | extendedKeyModifier;
  547. const int KeyPress::F1Key = VK_F1 | extendedKeyModifier;
  548. const int KeyPress::F2Key = VK_F2 | extendedKeyModifier;
  549. const int KeyPress::F3Key = VK_F3 | extendedKeyModifier;
  550. const int KeyPress::F4Key = VK_F4 | extendedKeyModifier;
  551. const int KeyPress::F5Key = VK_F5 | extendedKeyModifier;
  552. const int KeyPress::F6Key = VK_F6 | extendedKeyModifier;
  553. const int KeyPress::F7Key = VK_F7 | extendedKeyModifier;
  554. const int KeyPress::F8Key = VK_F8 | extendedKeyModifier;
  555. const int KeyPress::F9Key = VK_F9 | extendedKeyModifier;
  556. const int KeyPress::F10Key = VK_F10 | extendedKeyModifier;
  557. const int KeyPress::F11Key = VK_F11 | extendedKeyModifier;
  558. const int KeyPress::F12Key = VK_F12 | extendedKeyModifier;
  559. const int KeyPress::F13Key = VK_F13 | extendedKeyModifier;
  560. const int KeyPress::F14Key = VK_F14 | extendedKeyModifier;
  561. const int KeyPress::F15Key = VK_F15 | extendedKeyModifier;
  562. const int KeyPress::F16Key = VK_F16 | extendedKeyModifier;
  563. const int KeyPress::F17Key = VK_F17 | extendedKeyModifier;
  564. const int KeyPress::F18Key = VK_F18 | extendedKeyModifier;
  565. const int KeyPress::F19Key = VK_F19 | extendedKeyModifier;
  566. const int KeyPress::F20Key = VK_F20 | extendedKeyModifier;
  567. const int KeyPress::F21Key = VK_F21 | extendedKeyModifier;
  568. const int KeyPress::F22Key = VK_F22 | extendedKeyModifier;
  569. const int KeyPress::F23Key = VK_F23 | extendedKeyModifier;
  570. const int KeyPress::F24Key = VK_F24 | extendedKeyModifier;
  571. const int KeyPress::F25Key = 0x31000; // Windows doesn't support F-keys 25 or higher
  572. const int KeyPress::F26Key = 0x31001;
  573. const int KeyPress::F27Key = 0x31002;
  574. const int KeyPress::F28Key = 0x31003;
  575. const int KeyPress::F29Key = 0x31004;
  576. const int KeyPress::F30Key = 0x31005;
  577. const int KeyPress::F31Key = 0x31006;
  578. const int KeyPress::F32Key = 0x31007;
  579. const int KeyPress::F33Key = 0x31008;
  580. const int KeyPress::F34Key = 0x31009;
  581. const int KeyPress::F35Key = 0x3100a;
  582. const int KeyPress::numberPad0 = VK_NUMPAD0 | extendedKeyModifier;
  583. const int KeyPress::numberPad1 = VK_NUMPAD1 | extendedKeyModifier;
  584. const int KeyPress::numberPad2 = VK_NUMPAD2 | extendedKeyModifier;
  585. const int KeyPress::numberPad3 = VK_NUMPAD3 | extendedKeyModifier;
  586. const int KeyPress::numberPad4 = VK_NUMPAD4 | extendedKeyModifier;
  587. const int KeyPress::numberPad5 = VK_NUMPAD5 | extendedKeyModifier;
  588. const int KeyPress::numberPad6 = VK_NUMPAD6 | extendedKeyModifier;
  589. const int KeyPress::numberPad7 = VK_NUMPAD7 | extendedKeyModifier;
  590. const int KeyPress::numberPad8 = VK_NUMPAD8 | extendedKeyModifier;
  591. const int KeyPress::numberPad9 = VK_NUMPAD9 | extendedKeyModifier;
  592. const int KeyPress::numberPadAdd = VK_ADD | extendedKeyModifier;
  593. const int KeyPress::numberPadSubtract = VK_SUBTRACT | extendedKeyModifier;
  594. const int KeyPress::numberPadMultiply = VK_MULTIPLY | extendedKeyModifier;
  595. const int KeyPress::numberPadDivide = VK_DIVIDE | extendedKeyModifier;
  596. const int KeyPress::numberPadSeparator = VK_SEPARATOR | extendedKeyModifier;
  597. const int KeyPress::numberPadDecimalPoint = VK_DECIMAL | extendedKeyModifier;
  598. const int KeyPress::numberPadEquals = 0x92 /*VK_OEM_NEC_EQUAL*/ | extendedKeyModifier;
  599. const int KeyPress::numberPadDelete = VK_DELETE | extendedKeyModifier;
  600. const int KeyPress::playKey = 0x30000;
  601. const int KeyPress::stopKey = 0x30001;
  602. const int KeyPress::fastForwardKey = 0x30002;
  603. const int KeyPress::rewindKey = 0x30003;
  604. //==============================================================================
  605. class WindowsBitmapImage : public ImagePixelData
  606. {
  607. public:
  608. WindowsBitmapImage (const Image::PixelFormat format,
  609. const int w, const int h, const bool clearImage)
  610. : ImagePixelData (format, w, h)
  611. {
  612. jassert (format == Image::RGB || format == Image::ARGB);
  613. static bool alwaysUse32Bits = isGraphicsCard32Bit(); // NB: for 32-bit cards, it's faster to use a 32-bit image.
  614. pixelStride = (alwaysUse32Bits || format == Image::ARGB) ? 4 : 3;
  615. lineStride = -((w * pixelStride + 3) & ~3);
  616. zerostruct (bitmapInfo);
  617. bitmapInfo.bV4Size = sizeof (BITMAPV4HEADER);
  618. bitmapInfo.bV4Width = w;
  619. bitmapInfo.bV4Height = h;
  620. bitmapInfo.bV4Planes = 1;
  621. bitmapInfo.bV4CSType = 1;
  622. bitmapInfo.bV4BitCount = (unsigned short) (pixelStride * 8);
  623. if (format == Image::ARGB)
  624. {
  625. bitmapInfo.bV4AlphaMask = 0xff000000;
  626. bitmapInfo.bV4RedMask = 0xff0000;
  627. bitmapInfo.bV4GreenMask = 0xff00;
  628. bitmapInfo.bV4BlueMask = 0xff;
  629. bitmapInfo.bV4V4Compression = BI_BITFIELDS;
  630. }
  631. else
  632. {
  633. bitmapInfo.bV4V4Compression = BI_RGB;
  634. }
  635. HDC dc = GetDC (nullptr);
  636. hdc = CreateCompatibleDC (dc);
  637. ReleaseDC (nullptr, dc);
  638. SetMapMode (hdc, MM_TEXT);
  639. hBitmap = CreateDIBSection (hdc, (BITMAPINFO*) &(bitmapInfo), DIB_RGB_COLORS,
  640. (void**) &bitmapData, nullptr, 0);
  641. previousBitmap = SelectObject (hdc, hBitmap);
  642. if (format == Image::ARGB && clearImage)
  643. zeromem (bitmapData, (size_t) std::abs (h * lineStride));
  644. imageData = bitmapData - (lineStride * (h - 1));
  645. }
  646. ~WindowsBitmapImage()
  647. {
  648. SelectObject (hdc, previousBitmap); // Selecting the previous bitmap before deleting the DC avoids a warning in BoundsChecker
  649. DeleteDC (hdc);
  650. DeleteObject (hBitmap);
  651. }
  652. std::unique_ptr<ImageType> createType() const override { return std::make_unique<NativeImageType>(); }
  653. std::unique_ptr<LowLevelGraphicsContext> createLowLevelContext() override
  654. {
  655. sendDataChangeMessage();
  656. return std::make_unique<LowLevelGraphicsSoftwareRenderer> (Image (this));
  657. }
  658. void initialiseBitmapData (Image::BitmapData& bitmap, int x, int y, Image::BitmapData::ReadWriteMode mode) override
  659. {
  660. bitmap.data = imageData + x * pixelStride + y * lineStride;
  661. bitmap.pixelFormat = pixelFormat;
  662. bitmap.lineStride = lineStride;
  663. bitmap.pixelStride = pixelStride;
  664. if (mode != Image::BitmapData::readOnly)
  665. sendDataChangeMessage();
  666. }
  667. ImagePixelData::Ptr clone() override
  668. {
  669. auto im = new WindowsBitmapImage (pixelFormat, width, height, false);
  670. for (int i = 0; i < height; ++i)
  671. memcpy (im->imageData + i * lineStride, imageData + i * lineStride, (size_t) lineStride);
  672. return im;
  673. }
  674. void blitToWindow (HWND hwnd, HDC dc, bool transparent, int x, int y, uint8 updateLayeredWindowAlpha) noexcept
  675. {
  676. SetMapMode (dc, MM_TEXT);
  677. if (transparent)
  678. {
  679. auto windowBounds = getWindowRect (hwnd);
  680. POINT p = { -x, -y };
  681. POINT pos = { windowBounds.left, windowBounds.top };
  682. SIZE size = { windowBounds.right - windowBounds.left,
  683. windowBounds.bottom - windowBounds.top };
  684. BLENDFUNCTION bf;
  685. bf.AlphaFormat = 1 /*AC_SRC_ALPHA*/;
  686. bf.BlendFlags = 0;
  687. bf.BlendOp = AC_SRC_OVER;
  688. bf.SourceConstantAlpha = updateLayeredWindowAlpha;
  689. UpdateLayeredWindow (hwnd, nullptr, &pos, &size, hdc, &p, 0, &bf, 2 /*ULW_ALPHA*/);
  690. }
  691. else
  692. {
  693. StretchDIBits (dc,
  694. x, y, width, height,
  695. 0, 0, width, height,
  696. bitmapData, (const BITMAPINFO*) &bitmapInfo,
  697. DIB_RGB_COLORS, SRCCOPY);
  698. }
  699. }
  700. HBITMAP hBitmap;
  701. HGDIOBJ previousBitmap;
  702. BITMAPV4HEADER bitmapInfo;
  703. HDC hdc;
  704. uint8* bitmapData;
  705. int pixelStride, lineStride;
  706. uint8* imageData;
  707. private:
  708. static bool isGraphicsCard32Bit()
  709. {
  710. auto dc = GetDC (nullptr);
  711. auto bitsPerPixel = GetDeviceCaps (dc, BITSPIXEL);
  712. ReleaseDC (nullptr, dc);
  713. return bitsPerPixel > 24;
  714. }
  715. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WindowsBitmapImage)
  716. };
  717. //==============================================================================
  718. Image createSnapshotOfNativeWindow (void*);
  719. Image createSnapshotOfNativeWindow (void* nativeWindowHandle)
  720. {
  721. auto hwnd = (HWND) nativeWindowHandle;
  722. auto r = convertPhysicalScreenRectangleToLogical (rectangleFromRECT (getWindowRect (hwnd)), hwnd);
  723. const int w = r.getWidth();
  724. const int h = r.getHeight();
  725. auto nativeBitmap = new WindowsBitmapImage (Image::RGB, w, h, true);
  726. Image bitmap (nativeBitmap);
  727. HDC dc = GetDC (hwnd);
  728. if (isPerMonitorDPIAwareProcess())
  729. {
  730. auto scale = getScaleFactorForWindow (hwnd);
  731. auto prevStretchMode = SetStretchBltMode (nativeBitmap->hdc, HALFTONE);
  732. SetBrushOrgEx (nativeBitmap->hdc, 0, 0, NULL);
  733. StretchBlt (nativeBitmap->hdc, 0, 0, w, h,
  734. dc, 0, 0, roundToInt (w * scale), roundToInt (h * scale),
  735. SRCCOPY);
  736. SetStretchBltMode (nativeBitmap->hdc, prevStretchMode);
  737. }
  738. else
  739. {
  740. BitBlt (nativeBitmap->hdc, 0, 0, w, h, dc, 0, 0, SRCCOPY);
  741. }
  742. ReleaseDC (hwnd, dc);
  743. return SoftwareImageType().convert (bitmap);
  744. }
  745. //==============================================================================
  746. namespace IconConverters
  747. {
  748. Image createImageFromHICON (HICON icon)
  749. {
  750. if (icon == nullptr)
  751. return {};
  752. struct ScopedICONINFO : public ICONINFO
  753. {
  754. ScopedICONINFO()
  755. {
  756. hbmColor = nullptr;
  757. hbmMask = nullptr;
  758. }
  759. ~ScopedICONINFO()
  760. {
  761. if (hbmColor != nullptr)
  762. ::DeleteObject (hbmColor);
  763. if (hbmMask != nullptr)
  764. ::DeleteObject (hbmMask);
  765. }
  766. };
  767. ScopedICONINFO info;
  768. if (! SUCCEEDED (::GetIconInfo (icon, &info)))
  769. return {};
  770. BITMAP bm;
  771. if (! (::GetObject (info.hbmColor, sizeof (BITMAP), &bm)
  772. && bm.bmWidth > 0 && bm.bmHeight > 0))
  773. return {};
  774. if (auto* tempDC = ::GetDC (nullptr))
  775. {
  776. if (auto* dc = ::CreateCompatibleDC (tempDC))
  777. {
  778. BITMAPV5HEADER header = {};
  779. header.bV5Size = sizeof (BITMAPV5HEADER);
  780. header.bV5Width = bm.bmWidth;
  781. header.bV5Height = -bm.bmHeight;
  782. header.bV5Planes = 1;
  783. header.bV5Compression = BI_RGB;
  784. header.bV5BitCount = 32;
  785. header.bV5RedMask = 0x00FF0000;
  786. header.bV5GreenMask = 0x0000FF00;
  787. header.bV5BlueMask = 0x000000FF;
  788. header.bV5AlphaMask = 0xFF000000;
  789. header.bV5CSType = LCS_WINDOWS_COLOR_SPACE;
  790. header.bV5Intent = LCS_GM_IMAGES;
  791. uint32* bitmapImageData = nullptr;
  792. if (auto* dib = ::CreateDIBSection (tempDC, (BITMAPINFO*) &header, DIB_RGB_COLORS,
  793. (void**) &bitmapImageData, nullptr, 0))
  794. {
  795. auto oldObject = ::SelectObject (dc, dib);
  796. auto numPixels = bm.bmWidth * bm.bmHeight;
  797. auto numColourComponents = (size_t) numPixels * 4;
  798. // Windows icon data comes as two layers, an XOR mask which contains the bulk
  799. // of the image data and an AND mask which provides the transparency. Annoyingly
  800. // the XOR mask can also contain an alpha channel, in which case the transparency
  801. // mask should not be applied, but there's no way to find out a priori if the XOR
  802. // mask contains an alpha channel.
  803. HeapBlock<bool> opacityMask (numPixels);
  804. memset (bitmapImageData, 0, numColourComponents);
  805. ::DrawIconEx (dc, 0, 0, icon, bm.bmWidth, bm.bmHeight, 0, nullptr, DI_MASK);
  806. for (int i = 0; i < numPixels; ++i)
  807. opacityMask[i] = (bitmapImageData[i] == 0);
  808. Image result = Image (Image::ARGB, bm.bmWidth, bm.bmHeight, true);
  809. Image::BitmapData imageData (result, Image::BitmapData::readWrite);
  810. memset (bitmapImageData, 0, numColourComponents);
  811. ::DrawIconEx (dc, 0, 0, icon, bm.bmWidth, bm.bmHeight, 0, nullptr, DI_NORMAL);
  812. memcpy (imageData.data, bitmapImageData, numColourComponents);
  813. auto imageHasAlphaChannel = [&imageData, numPixels]()
  814. {
  815. for (int i = 0; i < numPixels; ++i)
  816. if (imageData.data[i * 4] != 0)
  817. return true;
  818. return false;
  819. };
  820. if (! imageHasAlphaChannel())
  821. for (int i = 0; i < numPixels; ++i)
  822. imageData.data[i * 4] = opacityMask[i] ? 0xff : 0x00;
  823. ::SelectObject (dc, oldObject);
  824. ::DeleteObject(dib);
  825. ::DeleteDC (dc);
  826. ::ReleaseDC (nullptr, tempDC);
  827. return result;
  828. }
  829. ::DeleteDC (dc);
  830. }
  831. ::ReleaseDC (nullptr, tempDC);
  832. }
  833. return {};
  834. }
  835. HICON createHICONFromImage (const Image& image, const BOOL isIcon, int hotspotX, int hotspotY)
  836. {
  837. auto nativeBitmap = new WindowsBitmapImage (Image::ARGB, image.getWidth(), image.getHeight(), true);
  838. Image bitmap (nativeBitmap);
  839. {
  840. Graphics g (bitmap);
  841. g.drawImageAt (image, 0, 0);
  842. }
  843. auto mask = CreateBitmap (image.getWidth(), image.getHeight(), 1, 1, nullptr);
  844. ICONINFO info;
  845. info.fIcon = isIcon;
  846. info.xHotspot = (DWORD) hotspotX;
  847. info.yHotspot = (DWORD) hotspotY;
  848. info.hbmMask = mask;
  849. info.hbmColor = nativeBitmap->hBitmap;
  850. auto hi = CreateIconIndirect (&info);
  851. DeleteObject (mask);
  852. return hi;
  853. }
  854. }
  855. //==============================================================================
  856. JUCE_COMCLASS (ITipInvocation, "37c994e7-432b-4834-a2f7-dce1f13b834b") : public IUnknown
  857. {
  858. static CLSID getCLSID() noexcept { return { 0x4ce576fa, 0x83dc, 0x4f88, { 0x95, 0x1c, 0x9d, 0x07, 0x82, 0xb4, 0xe3, 0x76 } }; }
  859. virtual HRESULT STDMETHODCALLTYPE Toggle (HWND) = 0;
  860. };
  861. struct OnScreenKeyboard : public DeletedAtShutdown,
  862. private Timer
  863. {
  864. void activate()
  865. {
  866. shouldBeActive = true;
  867. startTimer (10);
  868. }
  869. void deactivate()
  870. {
  871. shouldBeActive = false;
  872. startTimer (10);
  873. }
  874. JUCE_DECLARE_SINGLETON_SINGLETHREADED (OnScreenKeyboard, false)
  875. private:
  876. OnScreenKeyboard()
  877. {
  878. tipInvocation.CoCreateInstance (ITipInvocation::getCLSID(), CLSCTX_INPROC_HANDLER | CLSCTX_LOCAL_SERVER);
  879. }
  880. ~OnScreenKeyboard()
  881. {
  882. clearSingletonInstance();
  883. }
  884. void timerCallback() override
  885. {
  886. stopTimer();
  887. if (reentrant || tipInvocation == nullptr)
  888. return;
  889. const ScopedValueSetter<bool> setter (reentrant, true, false);
  890. auto isActive = isKeyboardVisible();
  891. if (isActive != shouldBeActive)
  892. {
  893. if (! isActive)
  894. {
  895. tipInvocation->Toggle (GetDesktopWindow());
  896. }
  897. else
  898. {
  899. if (auto hwnd = FindWindow (L"IPTip_Main_Window", NULL))
  900. PostMessage (hwnd, WM_SYSCOMMAND, (int) SC_CLOSE, 0);
  901. }
  902. }
  903. }
  904. bool isVisible()
  905. {
  906. if (auto hwnd = FindWindowEx (NULL, NULL, L"ApplicationFrameWindow", NULL))
  907. return FindWindowEx (hwnd, NULL, L"Windows.UI.Core.CoreWindow", L"Microsoft Text Input Application") != NULL;
  908. return false;
  909. }
  910. bool isVisibleLegacy()
  911. {
  912. if (auto hwnd = FindWindow (L"IPTip_Main_Window", NULL))
  913. {
  914. auto style = GetWindowLong (hwnd, GWL_STYLE);
  915. return (style & WS_DISABLED) == 0 && (style & WS_VISIBLE) != 0;
  916. }
  917. return false;
  918. }
  919. bool isKeyboardVisible()
  920. {
  921. if (isVisible())
  922. return true;
  923. // isVisible() may fail on Win10 versions < 1709 so try the old method too
  924. return isVisibleLegacy();
  925. }
  926. bool shouldBeActive = false, reentrant = false;
  927. ComSmartPtr<ITipInvocation> tipInvocation;
  928. };
  929. JUCE_IMPLEMENT_SINGLETON (OnScreenKeyboard)
  930. //==============================================================================
  931. struct HSTRING_PRIVATE;
  932. typedef HSTRING_PRIVATE* HSTRING;
  933. struct IInspectable : public IUnknown
  934. {
  935. virtual HRESULT STDMETHODCALLTYPE GetIids (ULONG* ,IID**) = 0;
  936. virtual HRESULT STDMETHODCALLTYPE GetRuntimeClassName (HSTRING*) = 0;
  937. virtual HRESULT STDMETHODCALLTYPE GetTrustLevel (void*) = 0;
  938. };
  939. JUCE_COMCLASS (IUIViewSettingsInterop, "3694dbf9-8f68-44be-8ff5-195c98ede8a6") : public IInspectable
  940. {
  941. virtual HRESULT STDMETHODCALLTYPE GetForWindow (HWND, REFIID, void**) = 0;
  942. };
  943. JUCE_COMCLASS (IUIViewSettings, "c63657f6-8850-470d-88f8-455e16ea2c26") : public IInspectable
  944. {
  945. enum UserInteractionMode
  946. {
  947. Mouse = 0,
  948. Touch = 1
  949. };
  950. virtual HRESULT STDMETHODCALLTYPE GetUserInteractionMode (UserInteractionMode*) = 0;
  951. };
  952. struct UWPUIViewSettings
  953. {
  954. UWPUIViewSettings()
  955. {
  956. ComBaseModule dll (L"api-ms-win-core-winrt-l1-1-0");
  957. if (dll.h != nullptr)
  958. {
  959. roInitialize = (RoInitializeFuncPtr) ::GetProcAddress (dll.h, "RoInitialize");
  960. roGetActivationFactory = (RoGetActivationFactoryFuncPtr) ::GetProcAddress (dll.h, "RoGetActivationFactory");
  961. createHString = (WindowsCreateStringFuncPtr) ::GetProcAddress (dll.h, "WindowsCreateString");
  962. deleteHString = (WindowsDeleteStringFuncPtr) ::GetProcAddress (dll.h, "WindowsDeleteString");
  963. if (roInitialize == nullptr || roGetActivationFactory == nullptr
  964. || createHString == nullptr || deleteHString == nullptr)
  965. return;
  966. auto status = roInitialize (1);
  967. if (status != S_OK && status != S_FALSE && (unsigned) status != 0x80010106L)
  968. return;
  969. LPCWSTR uwpClassName = L"Windows.UI.ViewManagement.UIViewSettings";
  970. HSTRING uwpClassId;
  971. if (createHString (uwpClassName, (::UINT32) wcslen (uwpClassName), &uwpClassId) != S_OK
  972. || uwpClassId == nullptr)
  973. return;
  974. status = roGetActivationFactory (uwpClassId, __uuidof (IUIViewSettingsInterop),
  975. (void**) viewSettingsInterop.resetAndGetPointerAddress());
  976. deleteHString (uwpClassId);
  977. if (status != S_OK || viewSettingsInterop == nullptr)
  978. return;
  979. // move dll into member var
  980. comBaseDLL = std::move (dll);
  981. }
  982. }
  983. bool isTabletModeActivatedForWindow (::HWND hWnd) const
  984. {
  985. if (viewSettingsInterop == nullptr)
  986. return false;
  987. ComSmartPtr<IUIViewSettings> viewSettings;
  988. if (viewSettingsInterop->GetForWindow (hWnd, __uuidof (IUIViewSettings),
  989. (void**) viewSettings.resetAndGetPointerAddress()) == S_OK
  990. && viewSettings != nullptr)
  991. {
  992. IUIViewSettings::UserInteractionMode mode;
  993. if (viewSettings->GetUserInteractionMode (&mode) == S_OK)
  994. return mode == IUIViewSettings::Touch;
  995. }
  996. return false;
  997. }
  998. private:
  999. //==============================================================================
  1000. struct ComBaseModule
  1001. {
  1002. ComBaseModule() {}
  1003. ComBaseModule (LPCWSTR libraryName) : h (::LoadLibrary (libraryName)) {}
  1004. ComBaseModule (ComBaseModule&& o) : h (o.h) { o.h = nullptr; }
  1005. ~ComBaseModule() { release(); }
  1006. void release() { if (h != nullptr) ::FreeLibrary (h); h = nullptr; }
  1007. ComBaseModule& operator= (ComBaseModule&& o) { release(); h = o.h; o.h = nullptr; return *this; }
  1008. HMODULE h = {};
  1009. };
  1010. using RoInitializeFuncPtr = HRESULT (WINAPI*) (int);
  1011. using RoGetActivationFactoryFuncPtr = HRESULT (WINAPI*) (HSTRING, REFIID, void**);
  1012. using WindowsCreateStringFuncPtr = HRESULT (WINAPI*) (LPCWSTR,UINT32, HSTRING*);
  1013. using WindowsDeleteStringFuncPtr = HRESULT (WINAPI*) (HSTRING);
  1014. ComBaseModule comBaseDLL;
  1015. ComSmartPtr<IUIViewSettingsInterop> viewSettingsInterop;
  1016. RoInitializeFuncPtr roInitialize;
  1017. RoGetActivationFactoryFuncPtr roGetActivationFactory;
  1018. WindowsCreateStringFuncPtr createHString;
  1019. WindowsDeleteStringFuncPtr deleteHString;
  1020. };
  1021. //==============================================================================
  1022. class HWNDComponentPeer : public ComponentPeer,
  1023. private Timer
  1024. #if JUCE_MODULE_AVAILABLE_juce_audio_plugin_client
  1025. , public ModifierKeyReceiver
  1026. #endif
  1027. {
  1028. public:
  1029. enum RenderingEngineType
  1030. {
  1031. softwareRenderingEngine = 0,
  1032. direct2DRenderingEngine
  1033. };
  1034. //==============================================================================
  1035. HWNDComponentPeer (Component& comp, int windowStyleFlags, HWND parent, bool nonRepainting)
  1036. : ComponentPeer (comp, windowStyleFlags),
  1037. dontRepaint (nonRepainting),
  1038. parentToAddTo (parent),
  1039. currentRenderingEngine (softwareRenderingEngine)
  1040. {
  1041. callFunctionIfNotLocked (&createWindowCallback, this);
  1042. setTitle (component.getName());
  1043. updateShadower();
  1044. // make sure that the on-screen keyboard code is loaded
  1045. OnScreenKeyboard::getInstance();
  1046. getNativeRealtimeModifiers = []
  1047. {
  1048. HWNDComponentPeer::updateKeyModifiers();
  1049. int mouseMods = 0;
  1050. if (HWNDComponentPeer::isKeyDown (VK_LBUTTON)) mouseMods |= ModifierKeys::leftButtonModifier;
  1051. if (HWNDComponentPeer::isKeyDown (VK_RBUTTON)) mouseMods |= ModifierKeys::rightButtonModifier;
  1052. if (HWNDComponentPeer::isKeyDown (VK_MBUTTON)) mouseMods |= ModifierKeys::middleButtonModifier;
  1053. ModifierKeys::currentModifiers = ModifierKeys::currentModifiers.withoutMouseButtons().withFlags (mouseMods);
  1054. return ModifierKeys::currentModifiers;
  1055. };
  1056. }
  1057. ~HWNDComponentPeer()
  1058. {
  1059. shadower = nullptr;
  1060. currentTouches.deleteAllTouchesForPeer (this);
  1061. // do this before the next bit to avoid messages arriving for this window
  1062. // before it's destroyed
  1063. JuceWindowIdentifier::setAsJUCEWindow (hwnd, false);
  1064. callFunctionIfNotLocked (&destroyWindowCallback, (void*) hwnd);
  1065. if (currentWindowIcon != nullptr)
  1066. DestroyIcon (currentWindowIcon);
  1067. if (dropTarget != nullptr)
  1068. {
  1069. dropTarget->peerIsDeleted = true;
  1070. dropTarget->Release();
  1071. dropTarget = nullptr;
  1072. }
  1073. #if JUCE_DIRECT2D
  1074. direct2DContext = nullptr;
  1075. #endif
  1076. }
  1077. //==============================================================================
  1078. void* getNativeHandle() const override { return hwnd; }
  1079. void setVisible (bool shouldBeVisible) override
  1080. {
  1081. ShowWindow (hwnd, shouldBeVisible ? SW_SHOWNA : SW_HIDE);
  1082. if (shouldBeVisible)
  1083. InvalidateRect (hwnd, nullptr, 0);
  1084. else
  1085. lastPaintTime = 0;
  1086. }
  1087. void setTitle (const String& title) override
  1088. {
  1089. // Unfortunately some ancient bits of win32 mean you can only perform this operation from the message thread.
  1090. JUCE_ASSERT_MESSAGE_THREAD
  1091. SetWindowText (hwnd, title.toWideCharPointer());
  1092. }
  1093. void repaintNowIfTransparent()
  1094. {
  1095. if (isUsingUpdateLayeredWindow() && lastPaintTime > 0 && Time::getMillisecondCounter() > lastPaintTime + 30)
  1096. handlePaintMessage();
  1097. }
  1098. void updateBorderSize()
  1099. {
  1100. WINDOWINFO info;
  1101. info.cbSize = sizeof (info);
  1102. if (GetWindowInfo (hwnd, &info))
  1103. windowBorder = BorderSize<int> (roundToInt ((info.rcClient.top - info.rcWindow.top) / scaleFactor),
  1104. roundToInt ((info.rcClient.left - info.rcWindow.left) / scaleFactor),
  1105. roundToInt ((info.rcWindow.bottom - info.rcClient.bottom) / scaleFactor),
  1106. roundToInt ((info.rcWindow.right - info.rcClient.right) / scaleFactor));
  1107. #if JUCE_DIRECT2D
  1108. if (direct2DContext != nullptr)
  1109. direct2DContext->resized();
  1110. #endif
  1111. }
  1112. void setBounds (const Rectangle<int>& bounds, bool isNowFullScreen) override
  1113. {
  1114. fullScreen = isNowFullScreen;
  1115. auto newBounds = windowBorder.addedTo (bounds);
  1116. if (isUsingUpdateLayeredWindow())
  1117. {
  1118. if (auto parentHwnd = GetParent (hwnd))
  1119. {
  1120. auto parentRect = convertPhysicalScreenRectangleToLogical (rectangleFromRECT (getWindowRect (parentHwnd)), hwnd);
  1121. newBounds.translate (parentRect.getX(), parentRect.getY());
  1122. }
  1123. }
  1124. auto oldBounds = getBounds();
  1125. const bool hasMoved = (oldBounds.getPosition() != bounds.getPosition());
  1126. const bool hasResized = (oldBounds.getWidth() != bounds.getWidth()
  1127. || oldBounds.getHeight() != bounds.getHeight());
  1128. DWORD flags = SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOOWNERZORDER;
  1129. if (! hasMoved) flags |= SWP_NOMOVE;
  1130. if (! hasResized) flags |= SWP_NOSIZE;
  1131. setWindowPos (hwnd, newBounds, flags, ! isInDPIChange);
  1132. if (hasResized && isValidPeer (this))
  1133. {
  1134. updateBorderSize();
  1135. repaintNowIfTransparent();
  1136. }
  1137. }
  1138. Rectangle<int> getBounds() const override
  1139. {
  1140. auto bounds = getWindowRect (hwnd);
  1141. if (auto parentH = GetParent (hwnd))
  1142. {
  1143. auto r = getWindowRect (parentH);
  1144. auto localBounds = Rectangle<int>::leftTopRightBottom (bounds.left, bounds.top,
  1145. bounds.right, bounds.bottom).translated (-r.left, -r.top);
  1146. #if JUCE_WIN_PER_MONITOR_DPI_AWARE
  1147. if (isPerMonitorDPIAwareWindow (hwnd))
  1148. localBounds = (localBounds.toDouble() / getPlatformScaleFactor()).toNearestInt();
  1149. #endif
  1150. return windowBorder.subtractedFrom (localBounds);
  1151. }
  1152. return windowBorder.subtractedFrom (convertPhysicalScreenRectangleToLogical (rectangleFromRECT (bounds), hwnd));
  1153. }
  1154. Point<int> getScreenPosition() const
  1155. {
  1156. auto r = convertPhysicalScreenRectangleToLogical (rectangleFromRECT (getWindowRect (hwnd)), hwnd);
  1157. return { r.getX() + windowBorder.getLeft(),
  1158. r.getY() + windowBorder.getTop() };
  1159. }
  1160. Point<float> localToGlobal (Point<float> relativePosition) override { return relativePosition + getScreenPosition().toFloat(); }
  1161. Point<float> globalToLocal (Point<float> screenPosition) override { return screenPosition - getScreenPosition().toFloat(); }
  1162. using ComponentPeer::localToGlobal;
  1163. using ComponentPeer::globalToLocal;
  1164. void setAlpha (float newAlpha) override
  1165. {
  1166. auto intAlpha = (uint8) jlimit (0, 255, (int) (newAlpha * 255.0f));
  1167. if (component.isOpaque())
  1168. {
  1169. if (newAlpha < 1.0f)
  1170. {
  1171. SetWindowLong (hwnd, GWL_EXSTYLE, GetWindowLong (hwnd, GWL_EXSTYLE) | WS_EX_LAYERED);
  1172. SetLayeredWindowAttributes (hwnd, RGB (0, 0, 0), intAlpha, LWA_ALPHA);
  1173. }
  1174. else
  1175. {
  1176. SetWindowLong (hwnd, GWL_EXSTYLE, GetWindowLong (hwnd, GWL_EXSTYLE) & ~WS_EX_LAYERED);
  1177. RedrawWindow (hwnd, nullptr, nullptr, RDW_ERASE | RDW_INVALIDATE | RDW_FRAME | RDW_ALLCHILDREN);
  1178. }
  1179. }
  1180. else
  1181. {
  1182. updateLayeredWindowAlpha = intAlpha;
  1183. component.repaint();
  1184. }
  1185. }
  1186. void setMinimised (bool shouldBeMinimised) override
  1187. {
  1188. if (shouldBeMinimised != isMinimised())
  1189. ShowWindow (hwnd, shouldBeMinimised ? SW_MINIMIZE : SW_SHOWNORMAL);
  1190. }
  1191. bool isMinimised() const override
  1192. {
  1193. WINDOWPLACEMENT wp;
  1194. wp.length = sizeof (WINDOWPLACEMENT);
  1195. GetWindowPlacement (hwnd, &wp);
  1196. return wp.showCmd == SW_SHOWMINIMIZED;
  1197. }
  1198. void setFullScreen (bool shouldBeFullScreen) override
  1199. {
  1200. setMinimised (false);
  1201. if (isFullScreen() != shouldBeFullScreen)
  1202. {
  1203. if (constrainer != nullptr)
  1204. constrainer->resizeStart();
  1205. fullScreen = shouldBeFullScreen;
  1206. const WeakReference<Component> deletionChecker (&component);
  1207. if (! fullScreen)
  1208. {
  1209. auto boundsCopy = lastNonFullscreenBounds;
  1210. if (hasTitleBar())
  1211. ShowWindow (hwnd, SW_SHOWNORMAL);
  1212. if (! boundsCopy.isEmpty())
  1213. setBounds (ScalingHelpers::scaledScreenPosToUnscaled (component, boundsCopy), false);
  1214. }
  1215. else
  1216. {
  1217. if (hasTitleBar())
  1218. ShowWindow (hwnd, SW_SHOWMAXIMIZED);
  1219. else
  1220. SendMessageW (hwnd, WM_SETTINGCHANGE, 0, 0);
  1221. }
  1222. if (deletionChecker != nullptr)
  1223. handleMovedOrResized();
  1224. if (constrainer != nullptr)
  1225. constrainer->resizeEnd();
  1226. }
  1227. }
  1228. bool isFullScreen() const override
  1229. {
  1230. if (! hasTitleBar())
  1231. return fullScreen;
  1232. WINDOWPLACEMENT wp;
  1233. wp.length = sizeof (wp);
  1234. GetWindowPlacement (hwnd, &wp);
  1235. return wp.showCmd == SW_SHOWMAXIMIZED;
  1236. }
  1237. bool contains (Point<int> localPos, bool trueIfInAChildWindow) const override
  1238. {
  1239. auto r = convertPhysicalScreenRectangleToLogical (rectangleFromRECT (getWindowRect (hwnd)), hwnd);
  1240. if (! r.withZeroOrigin().contains (localPos))
  1241. return false;
  1242. auto globalPos = localPos + getScreenPosition();
  1243. #if JUCE_WIN_PER_MONITOR_DPI_AWARE
  1244. if (isPerMonitorDPIAwareThread() || isPerMonitorDPIAwareWindow (hwnd))
  1245. globalPos = Desktop::getInstance().getDisplays().logicalToPhysical (globalPos);
  1246. #endif
  1247. auto w = WindowFromPoint (POINTFromPoint (globalPos));
  1248. return w == hwnd || (trueIfInAChildWindow && (IsChild (hwnd, w) != 0));
  1249. }
  1250. BorderSize<int> getFrameSize() const override
  1251. {
  1252. return windowBorder;
  1253. }
  1254. bool setAlwaysOnTop (bool alwaysOnTop) override
  1255. {
  1256. const bool oldDeactivate = shouldDeactivateTitleBar;
  1257. shouldDeactivateTitleBar = ((styleFlags & windowIsTemporary) == 0);
  1258. setWindowZOrder (hwnd, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST);
  1259. shouldDeactivateTitleBar = oldDeactivate;
  1260. if (shadower != nullptr)
  1261. handleBroughtToFront();
  1262. return true;
  1263. }
  1264. void toFront (bool makeActive) override
  1265. {
  1266. setMinimised (false);
  1267. const bool oldDeactivate = shouldDeactivateTitleBar;
  1268. shouldDeactivateTitleBar = ((styleFlags & windowIsTemporary) == 0);
  1269. callFunctionIfNotLocked (makeActive ? &toFrontCallback1 : &toFrontCallback2, hwnd);
  1270. shouldDeactivateTitleBar = oldDeactivate;
  1271. if (! makeActive)
  1272. {
  1273. // in this case a broughttofront call won't have occurred, so do it now..
  1274. handleBroughtToFront();
  1275. }
  1276. }
  1277. void toBehind (ComponentPeer* other) override
  1278. {
  1279. if (auto* otherPeer = dynamic_cast<HWNDComponentPeer*> (other))
  1280. {
  1281. setMinimised (false);
  1282. // Must be careful not to try to put a topmost window behind a normal one, or Windows
  1283. // promotes the normal one to be topmost!
  1284. if (component.isAlwaysOnTop() == otherPeer->getComponent().isAlwaysOnTop())
  1285. setWindowZOrder (hwnd, otherPeer->hwnd);
  1286. else if (otherPeer->getComponent().isAlwaysOnTop())
  1287. setWindowZOrder (hwnd, HWND_TOP);
  1288. }
  1289. else
  1290. {
  1291. jassertfalse; // wrong type of window?
  1292. }
  1293. }
  1294. bool isFocused() const override
  1295. {
  1296. return callFunctionIfNotLocked (&getFocusCallback, nullptr) == (void*) hwnd;
  1297. }
  1298. void grabFocus() override
  1299. {
  1300. const bool oldDeactivate = shouldDeactivateTitleBar;
  1301. shouldDeactivateTitleBar = ((styleFlags & windowIsTemporary) == 0);
  1302. callFunctionIfNotLocked (&setFocusCallback, hwnd);
  1303. shouldDeactivateTitleBar = oldDeactivate;
  1304. }
  1305. void textInputRequired (Point<int>, TextInputTarget&) override
  1306. {
  1307. if (! hasCreatedCaret)
  1308. {
  1309. hasCreatedCaret = true;
  1310. CreateCaret (hwnd, (HBITMAP) 1, 0, 0);
  1311. }
  1312. ShowCaret (hwnd);
  1313. SetCaretPos (0, 0);
  1314. if (uwpViewSettings.isTabletModeActivatedForWindow (hwnd))
  1315. OnScreenKeyboard::getInstance()->activate();
  1316. }
  1317. void dismissPendingTextInput() override
  1318. {
  1319. imeHandler.handleSetContext (hwnd, false);
  1320. if (uwpViewSettings.isTabletModeActivatedForWindow (hwnd))
  1321. OnScreenKeyboard::getInstance()->deactivate();
  1322. }
  1323. void repaint (const Rectangle<int>& area) override
  1324. {
  1325. auto scale = getPlatformScaleFactor();
  1326. #if JUCE_WIN_PER_MONITOR_DPI_AWARE
  1327. // if the calling thread is DPI-aware but we are invalidating a non-DPI aware window RECT, we actually have to
  1328. // divide the bounds by the scale factor as it will get multiplied for the virtualised paint callback...
  1329. if (isPerMonitorDPIAwareThread() && ! isPerMonitorDPIAwareWindow (hwnd))
  1330. scale = 1.0 / Desktop::getInstance().getDisplays().getMainDisplay().scale;
  1331. #endif
  1332. auto scaled = area.toDouble() * scale;
  1333. auto r = RECTFromRectangle (scaled.getSmallestIntegerContainer());
  1334. InvalidateRect (hwnd, &r, FALSE);
  1335. }
  1336. void performAnyPendingRepaintsNow() override
  1337. {
  1338. if (component.isVisible())
  1339. {
  1340. WeakReference<Component> localRef (&component);
  1341. MSG m;
  1342. if (isUsingUpdateLayeredWindow() || PeekMessage (&m, hwnd, WM_PAINT, WM_PAINT, PM_REMOVE))
  1343. if (localRef != nullptr) // (the PeekMessage call can dispatch messages, which may delete this comp)
  1344. handlePaintMessage();
  1345. }
  1346. }
  1347. //==============================================================================
  1348. static HWNDComponentPeer* getOwnerOfWindow (HWND h) noexcept
  1349. {
  1350. if (h != nullptr && JuceWindowIdentifier::isJUCEWindow (h))
  1351. return (HWNDComponentPeer*) GetWindowLongPtr (h, 8);
  1352. return nullptr;
  1353. }
  1354. //==============================================================================
  1355. bool isInside (HWND h) const noexcept
  1356. {
  1357. return GetAncestor (hwnd, GA_ROOT) == h;
  1358. }
  1359. //==============================================================================
  1360. static bool isKeyDown (const int key) noexcept { return (GetAsyncKeyState (key) & 0x8000) != 0; }
  1361. static void updateKeyModifiers() noexcept
  1362. {
  1363. int keyMods = 0;
  1364. if (isKeyDown (VK_SHIFT)) keyMods |= ModifierKeys::shiftModifier;
  1365. if (isKeyDown (VK_CONTROL)) keyMods |= ModifierKeys::ctrlModifier;
  1366. if (isKeyDown (VK_MENU)) keyMods |= ModifierKeys::altModifier;
  1367. // workaround: Windows maps AltGr to left-Ctrl + right-Alt.
  1368. if (isKeyDown (VK_RMENU) && !isKeyDown (VK_RCONTROL))
  1369. {
  1370. keyMods = (keyMods & ~ModifierKeys::ctrlModifier) | ModifierKeys::altModifier;
  1371. }
  1372. ModifierKeys::currentModifiers = ModifierKeys::currentModifiers.withOnlyMouseButtons().withFlags (keyMods);
  1373. }
  1374. static void updateModifiersFromWParam (const WPARAM wParam)
  1375. {
  1376. int mouseMods = 0;
  1377. if (wParam & MK_LBUTTON) mouseMods |= ModifierKeys::leftButtonModifier;
  1378. if (wParam & MK_RBUTTON) mouseMods |= ModifierKeys::rightButtonModifier;
  1379. if (wParam & MK_MBUTTON) mouseMods |= ModifierKeys::middleButtonModifier;
  1380. ModifierKeys::currentModifiers = ModifierKeys::currentModifiers.withoutMouseButtons().withFlags (mouseMods);
  1381. updateKeyModifiers();
  1382. }
  1383. //==============================================================================
  1384. bool dontRepaint;
  1385. static ModifierKeys modifiersAtLastCallback;
  1386. //==============================================================================
  1387. struct FileDropTarget : public ComBaseClassHelper<IDropTarget>
  1388. {
  1389. FileDropTarget (HWNDComponentPeer& p) : peer (p) {}
  1390. JUCE_COMRESULT DragEnter (IDataObject* pDataObject, DWORD grfKeyState, POINTL mousePos, DWORD* pdwEffect) override
  1391. {
  1392. auto hr = updateFileList (pDataObject);
  1393. if (FAILED (hr))
  1394. return hr;
  1395. return DragOver (grfKeyState, mousePos, pdwEffect);
  1396. }
  1397. JUCE_COMRESULT DragLeave() override
  1398. {
  1399. if (peerIsDeleted)
  1400. return S_FALSE;
  1401. peer.handleDragExit (dragInfo);
  1402. return S_OK;
  1403. }
  1404. JUCE_COMRESULT DragOver (DWORD /*grfKeyState*/, POINTL mousePos, DWORD* pdwEffect) override
  1405. {
  1406. if (peerIsDeleted)
  1407. return S_FALSE;
  1408. dragInfo.position = getMousePos (mousePos).roundToInt();
  1409. *pdwEffect = peer.handleDragMove (dragInfo) ? (DWORD) DROPEFFECT_COPY
  1410. : (DWORD) DROPEFFECT_NONE;
  1411. return S_OK;
  1412. }
  1413. JUCE_COMRESULT Drop (IDataObject* pDataObject, DWORD /*grfKeyState*/, POINTL mousePos, DWORD* pdwEffect) override
  1414. {
  1415. auto hr = updateFileList (pDataObject);
  1416. if (FAILED (hr))
  1417. return hr;
  1418. dragInfo.position = getMousePos (mousePos).roundToInt();
  1419. *pdwEffect = peer.handleDragDrop (dragInfo) ? (DWORD) DROPEFFECT_COPY
  1420. : (DWORD) DROPEFFECT_NONE;
  1421. return S_OK;
  1422. }
  1423. HWNDComponentPeer& peer;
  1424. ComponentPeer::DragInfo dragInfo;
  1425. bool peerIsDeleted = false;
  1426. private:
  1427. Point<float> getMousePos (POINTL mousePos) const
  1428. {
  1429. Point<float> screenPos;
  1430. #if JUCE_WIN_PER_MONITOR_DPI_AWARE
  1431. auto h = (HWND) peer.getNativeHandle();
  1432. if (isPerMonitorDPIAwareWindow (h))
  1433. screenPos = convertPhysicalScreenPointToLogical (pointFromPOINT ({ mousePos.x, mousePos.y }), h).toFloat();
  1434. else
  1435. #endif
  1436. screenPos = pointFromPOINT ({ mousePos.x, mousePos.y }).toFloat() / static_cast<float> (getGlobalDPI() / USER_DEFAULT_SCREEN_DPI);
  1437. return peer.getComponent().getLocalPoint (nullptr, screenPos);
  1438. }
  1439. template <typename CharType>
  1440. void parseFileList (const CharType* names, const SIZE_T totalLen)
  1441. {
  1442. for (unsigned int i = 0;;)
  1443. {
  1444. unsigned int len = 0;
  1445. while (i + len < totalLen && names[i + len] != 0)
  1446. ++len;
  1447. if (len == 0)
  1448. break;
  1449. dragInfo.files.add (String (names + i, len));
  1450. i += len + 1;
  1451. }
  1452. }
  1453. struct DroppedData
  1454. {
  1455. DroppedData (IDataObject* dataObject, CLIPFORMAT type)
  1456. {
  1457. FORMATETC format = { type, nullptr, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
  1458. STGMEDIUM resetMedium = { TYMED_HGLOBAL, { nullptr }, nullptr };
  1459. medium = resetMedium;
  1460. if (SUCCEEDED (error = dataObject->GetData (&format, &medium)))
  1461. {
  1462. dataSize = GlobalSize (medium.hGlobal);
  1463. data = GlobalLock (medium.hGlobal);
  1464. }
  1465. }
  1466. ~DroppedData()
  1467. {
  1468. if (data != nullptr)
  1469. GlobalUnlock (medium.hGlobal);
  1470. }
  1471. HRESULT error;
  1472. STGMEDIUM medium;
  1473. void* data = {};
  1474. SIZE_T dataSize;
  1475. };
  1476. HRESULT updateFileList (IDataObject* const dataObject)
  1477. {
  1478. if (peerIsDeleted)
  1479. return S_FALSE;
  1480. dragInfo.clear();
  1481. {
  1482. DroppedData fileData (dataObject, CF_HDROP);
  1483. if (SUCCEEDED (fileData.error))
  1484. {
  1485. auto dropFiles = static_cast<const LPDROPFILES> (fileData.data);
  1486. const void* const names = addBytesToPointer (dropFiles, sizeof (DROPFILES));
  1487. if (dropFiles->fWide)
  1488. parseFileList (static_cast<const WCHAR*> (names), fileData.dataSize);
  1489. else
  1490. parseFileList (static_cast<const char*> (names), fileData.dataSize);
  1491. return S_OK;
  1492. }
  1493. }
  1494. DroppedData textData (dataObject, CF_UNICODETEXT);
  1495. if (SUCCEEDED (textData.error))
  1496. {
  1497. dragInfo.text = String (CharPointer_UTF16 ((const WCHAR*) textData.data),
  1498. CharPointer_UTF16 ((const WCHAR*) addBytesToPointer (textData.data, textData.dataSize)));
  1499. return S_OK;
  1500. }
  1501. return textData.error;
  1502. }
  1503. JUCE_DECLARE_NON_COPYABLE (FileDropTarget)
  1504. };
  1505. static bool offerKeyMessageToJUCEWindow (MSG& m)
  1506. {
  1507. if (m.message == WM_KEYDOWN || m.message == WM_KEYUP)
  1508. if (Component::getCurrentlyFocusedComponent() != nullptr)
  1509. if (auto* h = getOwnerOfWindow (m.hwnd))
  1510. return m.message == WM_KEYDOWN ? h->doKeyDown (m.wParam)
  1511. : h->doKeyUp (m.wParam);
  1512. return false;
  1513. }
  1514. double getPlatformScaleFactor() const noexcept override
  1515. {
  1516. #if JUCE_WIN_PER_MONITOR_DPI_AWARE
  1517. if (! isPerMonitorDPIAwareWindow (hwnd))
  1518. return 1.0;
  1519. if (auto* parentHWND = GetParent (hwnd))
  1520. {
  1521. if (auto* parentPeer = getOwnerOfWindow (parentHWND))
  1522. return parentPeer->getPlatformScaleFactor();
  1523. if (getDPIForWindow != nullptr)
  1524. return getScaleFactorForWindow (parentHWND);
  1525. }
  1526. return scaleFactor;
  1527. #else
  1528. return 1.0;
  1529. #endif
  1530. }
  1531. private:
  1532. HWND hwnd, parentToAddTo;
  1533. std::unique_ptr<DropShadower> shadower;
  1534. RenderingEngineType currentRenderingEngine;
  1535. #if JUCE_DIRECT2D
  1536. std::unique_ptr<Direct2DLowLevelGraphicsContext> direct2DContext;
  1537. #endif
  1538. uint32 lastPaintTime = 0;
  1539. ULONGLONG lastMagnifySize = 0;
  1540. bool fullScreen = false, isDragging = false, isMouseOver = false,
  1541. hasCreatedCaret = false, constrainerIsResizing = false;
  1542. BorderSize<int> windowBorder;
  1543. HICON currentWindowIcon = nullptr;
  1544. FileDropTarget* dropTarget = nullptr;
  1545. uint8 updateLayeredWindowAlpha = 255;
  1546. UWPUIViewSettings uwpViewSettings;
  1547. #if JUCE_MODULE_AVAILABLE_juce_audio_plugin_client
  1548. ModifierKeyProvider* modProvider = nullptr;
  1549. #endif
  1550. double scaleFactor = 1.0;
  1551. bool isInDPIChange = false;
  1552. //==============================================================================
  1553. static MultiTouchMapper<DWORD> currentTouches;
  1554. //==============================================================================
  1555. struct TemporaryImage : private Timer
  1556. {
  1557. TemporaryImage() {}
  1558. Image& getImage (bool transparent, int w, int h)
  1559. {
  1560. auto format = transparent ? Image::ARGB : Image::RGB;
  1561. if ((! image.isValid()) || image.getWidth() < w || image.getHeight() < h || image.getFormat() != format)
  1562. image = Image (new WindowsBitmapImage (format, (w + 31) & ~31, (h + 31) & ~31, false));
  1563. startTimer (3000);
  1564. return image;
  1565. }
  1566. void timerCallback() override
  1567. {
  1568. stopTimer();
  1569. image = {};
  1570. }
  1571. private:
  1572. Image image;
  1573. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (TemporaryImage)
  1574. };
  1575. TemporaryImage offscreenImageGenerator;
  1576. //==============================================================================
  1577. class WindowClassHolder : private DeletedAtShutdown
  1578. {
  1579. public:
  1580. WindowClassHolder()
  1581. {
  1582. // this name has to be different for each app/dll instance because otherwise poor old Windows can
  1583. // get a bit confused (even despite it not being a process-global window class).
  1584. String windowClassName ("JUCE_");
  1585. windowClassName << String::toHexString (Time::currentTimeMillis());
  1586. auto moduleHandle = (HINSTANCE) Process::getCurrentModuleInstanceHandle();
  1587. TCHAR moduleFile[1024] = {};
  1588. GetModuleFileName (moduleHandle, moduleFile, 1024);
  1589. WORD iconNum = 0;
  1590. WNDCLASSEX wcex = {};
  1591. wcex.cbSize = sizeof (wcex);
  1592. wcex.style = CS_OWNDC;
  1593. wcex.lpfnWndProc = (WNDPROC) windowProc;
  1594. wcex.lpszClassName = windowClassName.toWideCharPointer();
  1595. wcex.cbWndExtra = 32;
  1596. wcex.hInstance = moduleHandle;
  1597. wcex.hIcon = ExtractAssociatedIcon (moduleHandle, moduleFile, &iconNum);
  1598. iconNum = 1;
  1599. wcex.hIconSm = ExtractAssociatedIcon (moduleHandle, moduleFile, &iconNum);
  1600. atom = RegisterClassEx (&wcex);
  1601. jassert (atom != 0);
  1602. isEventBlockedByModalComps = checkEventBlockedByModalComps;
  1603. }
  1604. ~WindowClassHolder()
  1605. {
  1606. if (ComponentPeer::getNumPeers() == 0)
  1607. UnregisterClass (getWindowClassName(), (HINSTANCE) Process::getCurrentModuleInstanceHandle());
  1608. clearSingletonInstance();
  1609. }
  1610. LPCTSTR getWindowClassName() const noexcept { return (LPCTSTR) (pointer_sized_uint) atom; }
  1611. JUCE_DECLARE_SINGLETON_SINGLETHREADED_MINIMAL (WindowClassHolder)
  1612. private:
  1613. ATOM atom;
  1614. static bool isHWNDBlockedByModalComponents (HWND h)
  1615. {
  1616. for (int i = Desktop::getInstance().getNumComponents(); --i >= 0;)
  1617. if (auto* c = Desktop::getInstance().getComponent (i))
  1618. if ((! c->isCurrentlyBlockedByAnotherModalComponent())
  1619. && IsChild ((HWND) c->getWindowHandle(), h))
  1620. return false;
  1621. return true;
  1622. }
  1623. static bool checkEventBlockedByModalComps (const MSG& m)
  1624. {
  1625. if (Component::getNumCurrentlyModalComponents() == 0 || JuceWindowIdentifier::isJUCEWindow (m.hwnd))
  1626. return false;
  1627. switch (m.message)
  1628. {
  1629. case WM_MOUSEMOVE:
  1630. case WM_NCMOUSEMOVE:
  1631. case 0x020A: /* WM_MOUSEWHEEL */
  1632. case 0x020E: /* WM_MOUSEHWHEEL */
  1633. case WM_KEYUP:
  1634. case WM_SYSKEYUP:
  1635. case WM_CHAR:
  1636. case WM_APPCOMMAND:
  1637. case WM_LBUTTONUP:
  1638. case WM_MBUTTONUP:
  1639. case WM_RBUTTONUP:
  1640. case WM_MOUSEACTIVATE:
  1641. case WM_NCMOUSEHOVER:
  1642. case WM_MOUSEHOVER:
  1643. case WM_TOUCH:
  1644. case WM_POINTERUPDATE:
  1645. case WM_NCPOINTERUPDATE:
  1646. case WM_POINTERWHEEL:
  1647. case WM_POINTERHWHEEL:
  1648. case WM_POINTERUP:
  1649. case WM_POINTERACTIVATE:
  1650. return isHWNDBlockedByModalComponents(m.hwnd);
  1651. case WM_NCLBUTTONDOWN:
  1652. case WM_NCLBUTTONDBLCLK:
  1653. case WM_NCRBUTTONDOWN:
  1654. case WM_NCRBUTTONDBLCLK:
  1655. case WM_NCMBUTTONDOWN:
  1656. case WM_NCMBUTTONDBLCLK:
  1657. case WM_LBUTTONDOWN:
  1658. case WM_LBUTTONDBLCLK:
  1659. case WM_MBUTTONDOWN:
  1660. case WM_MBUTTONDBLCLK:
  1661. case WM_RBUTTONDOWN:
  1662. case WM_RBUTTONDBLCLK:
  1663. case WM_KEYDOWN:
  1664. case WM_SYSKEYDOWN:
  1665. case WM_NCPOINTERDOWN:
  1666. case WM_POINTERDOWN:
  1667. if (isHWNDBlockedByModalComponents (m.hwnd))
  1668. {
  1669. if (auto* modal = Component::getCurrentlyModalComponent (0))
  1670. modal->inputAttemptWhenModal();
  1671. return true;
  1672. }
  1673. break;
  1674. default:
  1675. break;
  1676. }
  1677. return false;
  1678. }
  1679. JUCE_DECLARE_NON_COPYABLE (WindowClassHolder)
  1680. };
  1681. //==============================================================================
  1682. static void* createWindowCallback (void* userData)
  1683. {
  1684. static_cast<HWNDComponentPeer*> (userData)->createWindow();
  1685. return nullptr;
  1686. }
  1687. void createWindow()
  1688. {
  1689. DWORD exstyle = 0;
  1690. DWORD type = WS_CLIPSIBLINGS | WS_CLIPCHILDREN;
  1691. if (hasTitleBar())
  1692. {
  1693. type |= WS_OVERLAPPED;
  1694. if ((styleFlags & windowHasCloseButton) != 0)
  1695. {
  1696. type |= WS_SYSMENU;
  1697. }
  1698. else
  1699. {
  1700. // annoyingly, windows won't let you have a min/max button without a close button
  1701. jassert ((styleFlags & (windowHasMinimiseButton | windowHasMaximiseButton)) == 0);
  1702. }
  1703. if ((styleFlags & windowIsResizable) != 0)
  1704. type |= WS_THICKFRAME;
  1705. }
  1706. else if (parentToAddTo != nullptr)
  1707. {
  1708. type |= WS_CHILD;
  1709. }
  1710. else
  1711. {
  1712. type |= WS_POPUP | WS_SYSMENU;
  1713. }
  1714. if ((styleFlags & windowAppearsOnTaskbar) == 0)
  1715. exstyle |= WS_EX_TOOLWINDOW;
  1716. else
  1717. exstyle |= WS_EX_APPWINDOW;
  1718. if ((styleFlags & windowHasMinimiseButton) != 0) type |= WS_MINIMIZEBOX;
  1719. if ((styleFlags & windowHasMaximiseButton) != 0) type |= WS_MAXIMIZEBOX;
  1720. if ((styleFlags & windowIgnoresMouseClicks) != 0) exstyle |= WS_EX_TRANSPARENT;
  1721. if ((styleFlags & windowIsSemiTransparent) != 0) exstyle |= WS_EX_LAYERED;
  1722. hwnd = CreateWindowEx (exstyle, WindowClassHolder::getInstance()->getWindowClassName(),
  1723. L"", type, 0, 0, 0, 0, parentToAddTo, nullptr,
  1724. (HINSTANCE) Process::getCurrentModuleInstanceHandle(), nullptr);
  1725. if (hwnd != nullptr)
  1726. {
  1727. SetWindowLongPtr (hwnd, 0, 0);
  1728. SetWindowLongPtr (hwnd, 8, (LONG_PTR) this);
  1729. JuceWindowIdentifier::setAsJUCEWindow (hwnd, true);
  1730. if (dropTarget == nullptr)
  1731. {
  1732. HWNDComponentPeer* peer = nullptr;
  1733. if (dontRepaint)
  1734. peer = getOwnerOfWindow (parentToAddTo);
  1735. if (peer == nullptr)
  1736. peer = this;
  1737. dropTarget = new FileDropTarget (*peer);
  1738. }
  1739. RegisterDragDrop (hwnd, dropTarget);
  1740. if (canUseMultiTouch())
  1741. registerTouchWindow (hwnd, 0);
  1742. setDPIAwareness();
  1743. #if JUCE_WIN_PER_MONITOR_DPI_AWARE
  1744. if (isPerMonitorDPIAwareThread())
  1745. {
  1746. auto bounds = component.getBounds();
  1747. if (bounds.isEmpty())
  1748. scaleFactor = Desktop::getInstance().getDisplays().getMainDisplay().scale;
  1749. else
  1750. scaleFactor = Desktop::getInstance().getDisplays().findDisplayForRect (bounds).scale;
  1751. scaleFactor /= Desktop::getInstance().getGlobalScaleFactor();
  1752. }
  1753. #endif
  1754. setMessageFilter();
  1755. updateBorderSize();
  1756. checkForPointerAPI();
  1757. // This is needed so that our plugin window gets notified of WM_SETTINGCHANGE messages
  1758. // and can respond to display scale changes
  1759. if (! JUCEApplication::isStandaloneApp())
  1760. settingChangeCallback = forceDisplayUpdate;
  1761. // Calling this function here is (for some reason) necessary to make Windows
  1762. // correctly enable the menu items that we specify in the wm_initmenu message.
  1763. GetSystemMenu (hwnd, false);
  1764. auto alpha = component.getAlpha();
  1765. if (alpha < 1.0f)
  1766. setAlpha (alpha);
  1767. }
  1768. else
  1769. {
  1770. jassertfalse;
  1771. }
  1772. }
  1773. static BOOL CALLBACK revokeChildDragDropCallback (HWND hwnd, LPARAM) { RevokeDragDrop (hwnd); return TRUE; }
  1774. static void* destroyWindowCallback (void* handle)
  1775. {
  1776. auto hwnd = reinterpret_cast<HWND> (handle);
  1777. if (IsWindow (hwnd))
  1778. {
  1779. RevokeDragDrop (hwnd);
  1780. // NB: we need to do this before DestroyWindow() as child HWNDs will be invalid after
  1781. EnumChildWindows (hwnd, revokeChildDragDropCallback, 0);
  1782. DestroyWindow (hwnd);
  1783. }
  1784. return nullptr;
  1785. }
  1786. static void* toFrontCallback1 (void* h)
  1787. {
  1788. SetForegroundWindow ((HWND) h);
  1789. return nullptr;
  1790. }
  1791. static void* toFrontCallback2 (void* h)
  1792. {
  1793. setWindowZOrder ((HWND) h, HWND_TOP);
  1794. return nullptr;
  1795. }
  1796. static void* setFocusCallback (void* h)
  1797. {
  1798. SetFocus ((HWND) h);
  1799. return nullptr;
  1800. }
  1801. static void* getFocusCallback (void*)
  1802. {
  1803. return GetFocus();
  1804. }
  1805. bool isUsingUpdateLayeredWindow() const
  1806. {
  1807. return ! component.isOpaque();
  1808. }
  1809. bool hasTitleBar() const noexcept { return (styleFlags & windowHasTitleBar) != 0; }
  1810. void updateShadower()
  1811. {
  1812. if (! component.isCurrentlyModal() && (styleFlags & windowHasDropShadow) != 0
  1813. && ((! hasTitleBar()) || SystemStats::getOperatingSystemType() < SystemStats::WinVista))
  1814. {
  1815. shadower.reset (component.getLookAndFeel().createDropShadowerForComponent (&component));
  1816. if (shadower != nullptr)
  1817. shadower->setOwner (&component);
  1818. }
  1819. }
  1820. void setIcon (const Image& newIcon) override
  1821. {
  1822. if (auto hicon = IconConverters::createHICONFromImage (newIcon, TRUE, 0, 0))
  1823. {
  1824. SendMessage (hwnd, WM_SETICON, ICON_BIG, (LPARAM) hicon);
  1825. SendMessage (hwnd, WM_SETICON, ICON_SMALL, (LPARAM) hicon);
  1826. if (currentWindowIcon != nullptr)
  1827. DestroyIcon (currentWindowIcon);
  1828. currentWindowIcon = hicon;
  1829. }
  1830. }
  1831. void setMessageFilter()
  1832. {
  1833. using ChangeWindowMessageFilterExFunc = BOOL (WINAPI*) (HWND, UINT, DWORD, PVOID);
  1834. if (auto changeMessageFilter = (ChangeWindowMessageFilterExFunc) getUser32Function ("ChangeWindowMessageFilterEx"))
  1835. {
  1836. changeMessageFilter (hwnd, WM_DROPFILES, 1 /*MSGFLT_ALLOW*/, nullptr);
  1837. changeMessageFilter (hwnd, WM_COPYDATA, 1 /*MSGFLT_ALLOW*/, nullptr);
  1838. changeMessageFilter (hwnd, 0x49, 1 /*MSGFLT_ALLOW*/, nullptr);
  1839. }
  1840. }
  1841. struct ChildWindowClippingInfo
  1842. {
  1843. HDC dc;
  1844. HWNDComponentPeer* peer;
  1845. RectangleList<int>* clip;
  1846. Point<int> origin;
  1847. int savedDC;
  1848. };
  1849. static BOOL CALLBACK clipChildWindowCallback (HWND hwnd, LPARAM context)
  1850. {
  1851. if (IsWindowVisible (hwnd))
  1852. {
  1853. auto& info = *(ChildWindowClippingInfo*) context;
  1854. auto parent = GetParent (hwnd);
  1855. if (parent == info.peer->hwnd)
  1856. {
  1857. auto r = getWindowRect (hwnd);
  1858. POINT pos = { r.left, r.top };
  1859. ScreenToClient (GetParent (hwnd), &pos);
  1860. Rectangle<int> clip (pos.x, pos.y,
  1861. r.right - r.left,
  1862. r.bottom - r.top);
  1863. info.clip->subtract (clip - info.origin);
  1864. if (info.savedDC == 0)
  1865. info.savedDC = SaveDC (info.dc);
  1866. ExcludeClipRect (info.dc, clip.getX(), clip.getY(), clip.getRight(), clip.getBottom());
  1867. }
  1868. }
  1869. return TRUE;
  1870. }
  1871. //==============================================================================
  1872. void handlePaintMessage()
  1873. {
  1874. #if JUCE_DIRECT2D
  1875. if (direct2DContext != nullptr)
  1876. {
  1877. RECT r;
  1878. if (GetUpdateRect (hwnd, &r, false))
  1879. {
  1880. direct2DContext->start();
  1881. direct2DContext->clipToRectangle (convertPhysicalScreenRectangleToLogical (rectangleFromRECT (r), hwnd));
  1882. handlePaint (*direct2DContext);
  1883. direct2DContext->end();
  1884. ValidateRect (hwnd, &r);
  1885. }
  1886. }
  1887. else
  1888. #endif
  1889. {
  1890. HRGN rgn = CreateRectRgn (0, 0, 0, 0);
  1891. const int regionType = GetUpdateRgn (hwnd, rgn, false);
  1892. PAINTSTRUCT paintStruct;
  1893. HDC dc = BeginPaint (hwnd, &paintStruct); // Note this can immediately generate a WM_NCPAINT
  1894. // message and become re-entrant, but that's OK
  1895. // if something in a paint handler calls, e.g. a message box, this can become reentrant and
  1896. // corrupt the image it's using to paint into, so do a check here.
  1897. static bool reentrant = false;
  1898. if (! reentrant)
  1899. {
  1900. const ScopedValueSetter<bool> setter (reentrant, true, false);
  1901. if (dontRepaint)
  1902. component.handleCommandMessage (0); // (this triggers a repaint in the openGL context)
  1903. else
  1904. performPaint (dc, rgn, regionType, paintStruct);
  1905. }
  1906. DeleteObject (rgn);
  1907. EndPaint (hwnd, &paintStruct);
  1908. #if JUCE_MSVC
  1909. _fpreset(); // because some graphics cards can unmask FP exceptions
  1910. #endif
  1911. }
  1912. lastPaintTime = Time::getMillisecondCounter();
  1913. }
  1914. void performPaint (HDC dc, HRGN rgn, int regionType, PAINTSTRUCT& paintStruct)
  1915. {
  1916. int x = paintStruct.rcPaint.left;
  1917. int y = paintStruct.rcPaint.top;
  1918. int w = paintStruct.rcPaint.right - x;
  1919. int h = paintStruct.rcPaint.bottom - y;
  1920. const bool transparent = isUsingUpdateLayeredWindow();
  1921. if (transparent)
  1922. {
  1923. // it's not possible to have a transparent window with a title bar at the moment!
  1924. jassert (! hasTitleBar());
  1925. auto r = getWindowRect (hwnd);
  1926. x = y = 0;
  1927. w = r.right - r.left;
  1928. h = r.bottom - r.top;
  1929. }
  1930. if (w > 0 && h > 0)
  1931. {
  1932. Image& offscreenImage = offscreenImageGenerator.getImage (transparent, w, h);
  1933. RectangleList<int> contextClip;
  1934. const Rectangle<int> clipBounds (w, h);
  1935. bool needToPaintAll = true;
  1936. if (regionType == COMPLEXREGION && ! transparent)
  1937. {
  1938. HRGN clipRgn = CreateRectRgnIndirect (&paintStruct.rcPaint);
  1939. CombineRgn (rgn, rgn, clipRgn, RGN_AND);
  1940. DeleteObject (clipRgn);
  1941. char rgnData[8192];
  1942. const DWORD res = GetRegionData (rgn, sizeof (rgnData), (RGNDATA*) rgnData);
  1943. if (res > 0 && res <= sizeof (rgnData))
  1944. {
  1945. const RGNDATAHEADER* const hdr = &(((const RGNDATA*) rgnData)->rdh);
  1946. if (hdr->iType == RDH_RECTANGLES
  1947. && hdr->rcBound.right - hdr->rcBound.left >= w
  1948. && hdr->rcBound.bottom - hdr->rcBound.top >= h)
  1949. {
  1950. needToPaintAll = false;
  1951. auto rects = (const RECT*) (rgnData + sizeof (RGNDATAHEADER));
  1952. for (int i = (int) ((RGNDATA*) rgnData)->rdh.nCount; --i >= 0;)
  1953. {
  1954. if (rects->right <= x + w && rects->bottom <= y + h)
  1955. {
  1956. const int cx = jmax (x, (int) rects->left);
  1957. contextClip.addWithoutMerging (Rectangle<int> (cx - x, rects->top - y,
  1958. rects->right - cx, rects->bottom - rects->top)
  1959. .getIntersection (clipBounds));
  1960. }
  1961. else
  1962. {
  1963. needToPaintAll = true;
  1964. break;
  1965. }
  1966. ++rects;
  1967. }
  1968. }
  1969. }
  1970. }
  1971. if (needToPaintAll)
  1972. {
  1973. contextClip.clear();
  1974. contextClip.addWithoutMerging (Rectangle<int> (w, h));
  1975. }
  1976. ChildWindowClippingInfo childClipInfo = { dc, this, &contextClip, Point<int> (x, y), 0 };
  1977. EnumChildWindows (hwnd, clipChildWindowCallback, (LPARAM) &childClipInfo);
  1978. if (! contextClip.isEmpty())
  1979. {
  1980. if (transparent)
  1981. for (auto& i : contextClip)
  1982. offscreenImage.clear (i);
  1983. {
  1984. auto context = component.getLookAndFeel()
  1985. .createGraphicsContext (offscreenImage, { -x, -y }, contextClip);
  1986. context->addTransform (AffineTransform::scale ((float) getPlatformScaleFactor()));
  1987. handlePaint (*context);
  1988. }
  1989. static_cast<WindowsBitmapImage*> (offscreenImage.getPixelData())
  1990. ->blitToWindow (hwnd, dc, transparent, x, y, updateLayeredWindowAlpha);
  1991. }
  1992. if (childClipInfo.savedDC != 0)
  1993. RestoreDC (dc, childClipInfo.savedDC);
  1994. }
  1995. }
  1996. //==============================================================================
  1997. void doMouseEvent (Point<float> position, float pressure, float orientation = 0.0f, ModifierKeys mods = ModifierKeys::currentModifiers)
  1998. {
  1999. handleMouseEvent (MouseInputSource::InputSourceType::mouse, position, mods, pressure, orientation, getMouseEventTime());
  2000. }
  2001. StringArray getAvailableRenderingEngines() override
  2002. {
  2003. StringArray s ("Software Renderer");
  2004. #if JUCE_DIRECT2D
  2005. if (SystemStats::getOperatingSystemType() >= SystemStats::Windows7)
  2006. s.add ("Direct2D");
  2007. #endif
  2008. return s;
  2009. }
  2010. int getCurrentRenderingEngine() const override { return currentRenderingEngine; }
  2011. #if JUCE_DIRECT2D
  2012. void updateDirect2DContext()
  2013. {
  2014. if (currentRenderingEngine != direct2DRenderingEngine)
  2015. direct2DContext = nullptr;
  2016. else if (direct2DContext == nullptr)
  2017. direct2DContext.reset (new Direct2DLowLevelGraphicsContext (hwnd));
  2018. }
  2019. #endif
  2020. void setCurrentRenderingEngine (int index) override
  2021. {
  2022. ignoreUnused (index);
  2023. #if JUCE_DIRECT2D
  2024. if (getAvailableRenderingEngines().size() > 1)
  2025. {
  2026. currentRenderingEngine = index == 1 ? direct2DRenderingEngine : softwareRenderingEngine;
  2027. updateDirect2DContext();
  2028. repaint (component.getLocalBounds());
  2029. }
  2030. #endif
  2031. }
  2032. static uint32 getMinTimeBetweenMouseMoves()
  2033. {
  2034. if (SystemStats::getOperatingSystemType() >= SystemStats::WinVista)
  2035. return 0;
  2036. return 1000 / 60; // Throttling the incoming mouse-events seems to still be needed in XP..
  2037. }
  2038. bool isTouchEvent() noexcept
  2039. {
  2040. if (registerTouchWindow == nullptr)
  2041. return false;
  2042. // Relevant info about touch/pen detection flags:
  2043. // https://msdn.microsoft.com/en-us/library/windows/desktop/ms703320(v=vs.85).aspx
  2044. // http://www.petertissen.de/?p=4
  2045. return (GetMessageExtraInfo() & 0xFFFFFF80 /*SIGNATURE_MASK*/) == 0xFF515780 /*MI_WP_SIGNATURE*/;
  2046. }
  2047. static bool areOtherTouchSourcesActive()
  2048. {
  2049. for (auto& ms : Desktop::getInstance().getMouseSources())
  2050. if (ms.isDragging() && (ms.getType() == MouseInputSource::InputSourceType::touch
  2051. || ms.getType() == MouseInputSource::InputSourceType::pen))
  2052. return true;
  2053. return false;
  2054. }
  2055. void doMouseMove (Point<float> position, bool isMouseDownEvent)
  2056. {
  2057. ModifierKeys modsToSend (ModifierKeys::currentModifiers);
  2058. // this will be handled by WM_TOUCH
  2059. if (isTouchEvent() || areOtherTouchSourcesActive())
  2060. return;
  2061. if (! isMouseOver)
  2062. {
  2063. isMouseOver = true;
  2064. // This avoids a rare stuck-button problem when focus is lost unexpectedly, but must
  2065. // not be called as part of a move, in case it's actually a mouse-drag from another
  2066. // app which ends up here when we get focus before the mouse is released..
  2067. if (isMouseDownEvent && getNativeRealtimeModifiers != nullptr)
  2068. getNativeRealtimeModifiers();
  2069. updateKeyModifiers();
  2070. #if JUCE_MODULE_AVAILABLE_juce_audio_plugin_client
  2071. if (modProvider != nullptr)
  2072. ModifierKeys::currentModifiers = ModifierKeys::currentModifiers.withFlags (modProvider->getWin32Modifiers());
  2073. #endif
  2074. TRACKMOUSEEVENT tme;
  2075. tme.cbSize = sizeof (tme);
  2076. tme.dwFlags = TME_LEAVE;
  2077. tme.hwndTrack = hwnd;
  2078. tme.dwHoverTime = 0;
  2079. if (! TrackMouseEvent (&tme))
  2080. jassertfalse;
  2081. Desktop::getInstance().getMainMouseSource().forceMouseCursorUpdate();
  2082. }
  2083. else if (! isDragging)
  2084. {
  2085. if (! contains (position.roundToInt(), false))
  2086. return;
  2087. }
  2088. static uint32 lastMouseTime = 0;
  2089. static auto minTimeBetweenMouses = getMinTimeBetweenMouseMoves();
  2090. auto now = Time::getMillisecondCounter();
  2091. if (! Desktop::getInstance().getMainMouseSource().isDragging())
  2092. modsToSend = modsToSend.withoutMouseButtons();
  2093. if (now >= lastMouseTime + minTimeBetweenMouses)
  2094. {
  2095. lastMouseTime = now;
  2096. doMouseEvent (position, MouseInputSource::invalidPressure,
  2097. MouseInputSource::invalidOrientation, modsToSend);
  2098. }
  2099. }
  2100. void doMouseDown (Point<float> position, const WPARAM wParam)
  2101. {
  2102. // this will be handled by WM_TOUCH
  2103. if (isTouchEvent() || areOtherTouchSourcesActive())
  2104. return;
  2105. if (GetCapture() != hwnd)
  2106. SetCapture (hwnd);
  2107. doMouseMove (position, true);
  2108. if (isValidPeer (this))
  2109. {
  2110. updateModifiersFromWParam (wParam);
  2111. #if JUCE_MODULE_AVAILABLE_juce_audio_plugin_client
  2112. if (modProvider != nullptr)
  2113. ModifierKeys::currentModifiers = ModifierKeys::currentModifiers.withFlags (modProvider->getWin32Modifiers());
  2114. #endif
  2115. isDragging = true;
  2116. doMouseEvent (position, MouseInputSource::invalidPressure);
  2117. }
  2118. }
  2119. void doMouseUp (Point<float> position, const WPARAM wParam)
  2120. {
  2121. // this will be handled by WM_TOUCH
  2122. if (isTouchEvent() || areOtherTouchSourcesActive())
  2123. return;
  2124. updateModifiersFromWParam (wParam);
  2125. #if JUCE_MODULE_AVAILABLE_juce_audio_plugin_client
  2126. if (modProvider != nullptr)
  2127. ModifierKeys::currentModifiers = ModifierKeys::currentModifiers.withFlags (modProvider->getWin32Modifiers());
  2128. #endif
  2129. const bool wasDragging = isDragging;
  2130. isDragging = false;
  2131. // release the mouse capture if the user has released all buttons
  2132. if ((wParam & (MK_LBUTTON | MK_RBUTTON | MK_MBUTTON)) == 0 && hwnd == GetCapture())
  2133. ReleaseCapture();
  2134. // NB: under some circumstances (e.g. double-clicking a native title bar), a mouse-up can
  2135. // arrive without a mouse-down, so in that case we need to avoid sending a message.
  2136. if (wasDragging)
  2137. doMouseEvent (position, MouseInputSource::invalidPressure);
  2138. }
  2139. void doCaptureChanged()
  2140. {
  2141. if (constrainerIsResizing)
  2142. {
  2143. if (constrainer != nullptr)
  2144. constrainer->resizeEnd();
  2145. constrainerIsResizing = false;
  2146. }
  2147. if (isDragging)
  2148. doMouseUp (getCurrentMousePos(), (WPARAM) 0);
  2149. }
  2150. void doMouseExit()
  2151. {
  2152. isMouseOver = false;
  2153. if (! areOtherTouchSourcesActive())
  2154. doMouseEvent (getCurrentMousePos(), MouseInputSource::invalidPressure);
  2155. }
  2156. ComponentPeer* findPeerUnderMouse (Point<float>& localPos)
  2157. {
  2158. auto currentMousePos = getPOINTFromLParam (GetMessagePos());
  2159. // Because Windows stupidly sends all wheel events to the window with the keyboard
  2160. // focus, we have to redirect them here according to the mouse pos..
  2161. auto* peer = getOwnerOfWindow (WindowFromPoint (currentMousePos));
  2162. if (peer == nullptr)
  2163. peer = this;
  2164. localPos = peer->globalToLocal (convertPhysicalScreenPointToLogical (pointFromPOINT (currentMousePos), hwnd).toFloat());
  2165. return peer;
  2166. }
  2167. static MouseInputSource::InputSourceType getPointerType (WPARAM wParam)
  2168. {
  2169. if (getPointerTypeFunction != nullptr)
  2170. {
  2171. POINTER_INPUT_TYPE pointerType;
  2172. if (getPointerTypeFunction (GET_POINTERID_WPARAM (wParam), &pointerType))
  2173. {
  2174. if (pointerType == 2)
  2175. return MouseInputSource::InputSourceType::touch;
  2176. if (pointerType == 3)
  2177. return MouseInputSource::InputSourceType::pen;
  2178. }
  2179. }
  2180. return MouseInputSource::InputSourceType::mouse;
  2181. }
  2182. void doMouseWheel (const WPARAM wParam, const bool isVertical)
  2183. {
  2184. updateKeyModifiers();
  2185. const float amount = jlimit (-1000.0f, 1000.0f, 0.5f * (short) HIWORD (wParam));
  2186. MouseWheelDetails wheel;
  2187. wheel.deltaX = isVertical ? 0.0f : amount / -256.0f;
  2188. wheel.deltaY = isVertical ? amount / 256.0f : 0.0f;
  2189. wheel.isReversed = false;
  2190. wheel.isSmooth = false;
  2191. wheel.isInertial = false;
  2192. Point<float> localPos;
  2193. if (auto* peer = findPeerUnderMouse (localPos))
  2194. peer->handleMouseWheel (getPointerType (wParam), localPos, getMouseEventTime(), wheel);
  2195. }
  2196. bool doGestureEvent (LPARAM lParam)
  2197. {
  2198. GESTUREINFO gi;
  2199. zerostruct (gi);
  2200. gi.cbSize = sizeof (gi);
  2201. if (getGestureInfo != nullptr && getGestureInfo ((HGESTUREINFO) lParam, &gi))
  2202. {
  2203. updateKeyModifiers();
  2204. Point<float> localPos;
  2205. if (auto* peer = findPeerUnderMouse (localPos))
  2206. {
  2207. switch (gi.dwID)
  2208. {
  2209. case 3: /*GID_ZOOM*/
  2210. if (gi.dwFlags != 1 /*GF_BEGIN*/ && lastMagnifySize > 0)
  2211. peer->handleMagnifyGesture (MouseInputSource::InputSourceType::touch, localPos, getMouseEventTime(),
  2212. (float) (gi.ullArguments / (double) lastMagnifySize));
  2213. lastMagnifySize = gi.ullArguments;
  2214. return true;
  2215. case 4: /*GID_PAN*/
  2216. case 5: /*GID_ROTATE*/
  2217. case 6: /*GID_TWOFINGERTAP*/
  2218. case 7: /*GID_PRESSANDTAP*/
  2219. default:
  2220. break;
  2221. }
  2222. }
  2223. }
  2224. return false;
  2225. }
  2226. LRESULT doTouchEvent (const int numInputs, HTOUCHINPUT eventHandle)
  2227. {
  2228. if ((styleFlags & windowIgnoresMouseClicks) != 0)
  2229. if (auto* parent = getOwnerOfWindow (GetParent (hwnd)))
  2230. if (parent != this)
  2231. return parent->doTouchEvent (numInputs, eventHandle);
  2232. HeapBlock<TOUCHINPUT> inputInfo (numInputs);
  2233. if (getTouchInputInfo (eventHandle, (UINT) numInputs, inputInfo, sizeof (TOUCHINPUT)))
  2234. {
  2235. for (int i = 0; i < numInputs; ++i)
  2236. {
  2237. auto flags = inputInfo[i].dwFlags;
  2238. if ((flags & (TOUCHEVENTF_DOWN | TOUCHEVENTF_MOVE | TOUCHEVENTF_UP)) != 0)
  2239. if (! handleTouchInput (inputInfo[i], (flags & TOUCHEVENTF_DOWN) != 0, (flags & TOUCHEVENTF_UP) != 0))
  2240. return 0; // abandon method if this window was deleted by the callback
  2241. }
  2242. }
  2243. closeTouchInputHandle (eventHandle);
  2244. return 0;
  2245. }
  2246. bool handleTouchInput (const TOUCHINPUT& touch, const bool isDown, const bool isUp,
  2247. const float touchPressure = MouseInputSource::invalidPressure,
  2248. const float orientation = 0.0f)
  2249. {
  2250. auto isCancel = false;
  2251. const auto touchIndex = currentTouches.getIndexOfTouch (this, touch.dwID);
  2252. const auto time = getMouseEventTime();
  2253. const auto pos = globalToLocal (convertPhysicalScreenPointToLogical (pointFromPOINT ({ roundToInt (touch.x / 100.0f),
  2254. roundToInt (touch.y / 100.0f) }), hwnd).toFloat());
  2255. const auto pressure = touchPressure;
  2256. auto modsToSend = ModifierKeys::currentModifiers;
  2257. if (isDown)
  2258. {
  2259. ModifierKeys::currentModifiers = ModifierKeys::currentModifiers.withoutMouseButtons().withFlags (ModifierKeys::leftButtonModifier);
  2260. modsToSend = ModifierKeys::currentModifiers;
  2261. // this forces a mouse-enter/up event, in case for some reason we didn't get a mouse-up before.
  2262. handleMouseEvent (MouseInputSource::InputSourceType::touch, pos, modsToSend.withoutMouseButtons(),
  2263. pressure, orientation, time, {}, touchIndex);
  2264. if (! isValidPeer (this)) // (in case this component was deleted by the event)
  2265. return false;
  2266. }
  2267. else if (isUp)
  2268. {
  2269. modsToSend = modsToSend.withoutMouseButtons();
  2270. ModifierKeys::currentModifiers = modsToSend;
  2271. currentTouches.clearTouch (touchIndex);
  2272. if (! currentTouches.areAnyTouchesActive())
  2273. isCancel = true;
  2274. }
  2275. else
  2276. {
  2277. modsToSend = ModifierKeys::currentModifiers.withoutMouseButtons().withFlags (ModifierKeys::leftButtonModifier);
  2278. }
  2279. handleMouseEvent (MouseInputSource::InputSourceType::touch, pos, modsToSend,
  2280. pressure, orientation, time, {}, touchIndex);
  2281. if (! isValidPeer (this))
  2282. return false;
  2283. if (isUp)
  2284. {
  2285. handleMouseEvent (MouseInputSource::InputSourceType::touch, MouseInputSource::offscreenMousePos, ModifierKeys::currentModifiers.withoutMouseButtons(),
  2286. pressure, orientation, time, {}, touchIndex);
  2287. if (! isValidPeer (this))
  2288. return false;
  2289. if (isCancel)
  2290. {
  2291. currentTouches.clear();
  2292. ModifierKeys::currentModifiers = ModifierKeys::currentModifiers.withoutMouseButtons();
  2293. }
  2294. }
  2295. return true;
  2296. }
  2297. bool handlePointerInput (WPARAM wParam, LPARAM lParam, const bool isDown, const bool isUp)
  2298. {
  2299. if (! canUsePointerAPI)
  2300. return false;
  2301. auto pointerType = getPointerType (wParam);
  2302. if (pointerType == MouseInputSource::InputSourceType::touch)
  2303. {
  2304. POINTER_TOUCH_INFO touchInfo;
  2305. if (! getPointerTouchInfo (GET_POINTERID_WPARAM (wParam), &touchInfo))
  2306. return false;
  2307. const auto pressure = touchInfo.touchMask & TOUCH_MASK_PRESSURE ? touchInfo.pressure
  2308. : MouseInputSource::invalidPressure;
  2309. const auto orientation = touchInfo.touchMask & TOUCH_MASK_ORIENTATION ? degreesToRadians (static_cast<float> (touchInfo.orientation))
  2310. : MouseInputSource::invalidOrientation;
  2311. if (! handleTouchInput (emulateTouchEventFromPointer (touchInfo.pointerInfo.ptPixelLocationRaw, wParam),
  2312. isDown, isUp, pressure, orientation))
  2313. return false;
  2314. }
  2315. else if (pointerType == MouseInputSource::InputSourceType::pen)
  2316. {
  2317. POINTER_PEN_INFO penInfo;
  2318. if (! getPointerPenInfo (GET_POINTERID_WPARAM (wParam), &penInfo))
  2319. return false;
  2320. const auto pressure = (penInfo.penMask & PEN_MASK_PRESSURE) ? penInfo.pressure / 1024.0f : MouseInputSource::invalidPressure;
  2321. if (! handlePenInput (penInfo, globalToLocal (convertPhysicalScreenPointToLogical (pointFromPOINT (getPOINTFromLParam (lParam)), hwnd).toFloat()),
  2322. pressure, isDown, isUp))
  2323. return false;
  2324. }
  2325. else
  2326. {
  2327. return false;
  2328. }
  2329. return true;
  2330. }
  2331. TOUCHINPUT emulateTouchEventFromPointer (POINT p, WPARAM wParam)
  2332. {
  2333. TOUCHINPUT touchInput;
  2334. touchInput.dwID = GET_POINTERID_WPARAM (wParam);
  2335. touchInput.x = p.x * 100;
  2336. touchInput.y = p.y * 100;
  2337. return touchInput;
  2338. }
  2339. bool handlePenInput (POINTER_PEN_INFO penInfo, Point<float> pos, const float pressure, bool isDown, bool isUp)
  2340. {
  2341. const auto time = getMouseEventTime();
  2342. ModifierKeys modsToSend (ModifierKeys::currentModifiers);
  2343. PenDetails penDetails;
  2344. penDetails.rotation = (penInfo.penMask & PEN_MASK_ROTATION) ? degreesToRadians (static_cast<float> (penInfo.rotation)) : MouseInputSource::invalidRotation;
  2345. penDetails.tiltX = (penInfo.penMask & PEN_MASK_TILT_X) ? penInfo.tiltX / 90.0f : MouseInputSource::invalidTiltX;
  2346. penDetails.tiltY = (penInfo.penMask & PEN_MASK_TILT_Y) ? penInfo.tiltY / 90.0f : MouseInputSource::invalidTiltY;
  2347. auto pInfoFlags = penInfo.pointerInfo.pointerFlags;
  2348. if ((pInfoFlags & POINTER_FLAG_FIRSTBUTTON) != 0)
  2349. ModifierKeys::currentModifiers = ModifierKeys::currentModifiers.withoutMouseButtons().withFlags (ModifierKeys::leftButtonModifier);
  2350. else if ((pInfoFlags & POINTER_FLAG_SECONDBUTTON) != 0)
  2351. ModifierKeys::currentModifiers = ModifierKeys::currentModifiers.withoutMouseButtons().withFlags (ModifierKeys::rightButtonModifier);
  2352. if (isDown)
  2353. {
  2354. modsToSend = ModifierKeys::currentModifiers;
  2355. // this forces a mouse-enter/up event, in case for some reason we didn't get a mouse-up before.
  2356. handleMouseEvent (MouseInputSource::InputSourceType::pen, pos, modsToSend.withoutMouseButtons(),
  2357. pressure, MouseInputSource::invalidOrientation, time, penDetails);
  2358. if (! isValidPeer (this)) // (in case this component was deleted by the event)
  2359. return false;
  2360. }
  2361. else if (isUp || ! (pInfoFlags & POINTER_FLAG_INCONTACT))
  2362. {
  2363. modsToSend = modsToSend.withoutMouseButtons();
  2364. ModifierKeys::currentModifiers = ModifierKeys::currentModifiers.withoutMouseButtons();
  2365. }
  2366. handleMouseEvent (MouseInputSource::InputSourceType::pen, pos, modsToSend, pressure,
  2367. MouseInputSource::invalidOrientation, time, penDetails);
  2368. if (! isValidPeer (this)) // (in case this component was deleted by the event)
  2369. return false;
  2370. if (isUp)
  2371. {
  2372. handleMouseEvent (MouseInputSource::InputSourceType::pen, MouseInputSource::offscreenMousePos, ModifierKeys::currentModifiers,
  2373. pressure, MouseInputSource::invalidOrientation, time, penDetails);
  2374. if (! isValidPeer (this))
  2375. return false;
  2376. }
  2377. return true;
  2378. }
  2379. //==============================================================================
  2380. void sendModifierKeyChangeIfNeeded()
  2381. {
  2382. if (modifiersAtLastCallback != ModifierKeys::currentModifiers)
  2383. {
  2384. modifiersAtLastCallback = ModifierKeys::currentModifiers;
  2385. handleModifierKeysChange();
  2386. }
  2387. }
  2388. bool doKeyUp (const WPARAM key)
  2389. {
  2390. updateKeyModifiers();
  2391. switch (key)
  2392. {
  2393. case VK_SHIFT:
  2394. case VK_CONTROL:
  2395. case VK_MENU:
  2396. case VK_CAPITAL:
  2397. case VK_LWIN:
  2398. case VK_RWIN:
  2399. case VK_APPS:
  2400. case VK_NUMLOCK:
  2401. case VK_SCROLL:
  2402. case VK_LSHIFT:
  2403. case VK_RSHIFT:
  2404. case VK_LCONTROL:
  2405. case VK_LMENU:
  2406. case VK_RCONTROL:
  2407. case VK_RMENU:
  2408. sendModifierKeyChangeIfNeeded();
  2409. }
  2410. return handleKeyUpOrDown (false)
  2411. || Component::getCurrentlyModalComponent() != nullptr;
  2412. }
  2413. bool doKeyDown (const WPARAM key)
  2414. {
  2415. updateKeyModifiers();
  2416. bool used = false;
  2417. switch (key)
  2418. {
  2419. case VK_SHIFT:
  2420. case VK_LSHIFT:
  2421. case VK_RSHIFT:
  2422. case VK_CONTROL:
  2423. case VK_LCONTROL:
  2424. case VK_RCONTROL:
  2425. case VK_MENU:
  2426. case VK_LMENU:
  2427. case VK_RMENU:
  2428. case VK_LWIN:
  2429. case VK_RWIN:
  2430. case VK_CAPITAL:
  2431. case VK_NUMLOCK:
  2432. case VK_SCROLL:
  2433. case VK_APPS:
  2434. used = handleKeyUpOrDown (true);
  2435. sendModifierKeyChangeIfNeeded();
  2436. break;
  2437. case VK_LEFT:
  2438. case VK_RIGHT:
  2439. case VK_UP:
  2440. case VK_DOWN:
  2441. case VK_PRIOR:
  2442. case VK_NEXT:
  2443. case VK_HOME:
  2444. case VK_END:
  2445. case VK_DELETE:
  2446. case VK_INSERT:
  2447. case VK_F1:
  2448. case VK_F2:
  2449. case VK_F3:
  2450. case VK_F4:
  2451. case VK_F5:
  2452. case VK_F6:
  2453. case VK_F7:
  2454. case VK_F8:
  2455. case VK_F9:
  2456. case VK_F10:
  2457. case VK_F11:
  2458. case VK_F12:
  2459. case VK_F13:
  2460. case VK_F14:
  2461. case VK_F15:
  2462. case VK_F16:
  2463. case VK_F17:
  2464. case VK_F18:
  2465. case VK_F19:
  2466. case VK_F20:
  2467. case VK_F21:
  2468. case VK_F22:
  2469. case VK_F23:
  2470. case VK_F24:
  2471. used = handleKeyUpOrDown (true);
  2472. used = handleKeyPress (extendedKeyModifier | (int) key, 0) || used;
  2473. break;
  2474. default:
  2475. used = handleKeyUpOrDown (true);
  2476. {
  2477. MSG msg;
  2478. if (! PeekMessage (&msg, hwnd, WM_CHAR, WM_DEADCHAR, PM_NOREMOVE))
  2479. {
  2480. // if there isn't a WM_CHAR or WM_DEADCHAR message pending, we need to
  2481. // manually generate the key-press event that matches this key-down.
  2482. const UINT keyChar = MapVirtualKey ((UINT) key, 2);
  2483. const UINT scanCode = MapVirtualKey ((UINT) key, 0);
  2484. BYTE keyState[256];
  2485. GetKeyboardState (keyState);
  2486. WCHAR text[16] = { 0 };
  2487. if (ToUnicode ((UINT) key, scanCode, keyState, text, 8, 0) != 1)
  2488. text[0] = 0;
  2489. used = handleKeyPress ((int) LOWORD (keyChar), (juce_wchar) text[0]) || used;
  2490. }
  2491. }
  2492. break;
  2493. }
  2494. return used || (Component::getCurrentlyModalComponent() != nullptr);
  2495. }
  2496. bool doKeyChar (int key, const LPARAM flags)
  2497. {
  2498. updateKeyModifiers();
  2499. auto textChar = (juce_wchar) key;
  2500. const int virtualScanCode = (flags >> 16) & 0xff;
  2501. if (key >= '0' && key <= '9')
  2502. {
  2503. switch (virtualScanCode) // check for a numeric keypad scan-code
  2504. {
  2505. case 0x52:
  2506. case 0x4f:
  2507. case 0x50:
  2508. case 0x51:
  2509. case 0x4b:
  2510. case 0x4c:
  2511. case 0x4d:
  2512. case 0x47:
  2513. case 0x48:
  2514. case 0x49:
  2515. key = (key - '0') + KeyPress::numberPad0;
  2516. break;
  2517. default:
  2518. break;
  2519. }
  2520. }
  2521. else
  2522. {
  2523. // convert the scan code to an unmodified character code..
  2524. const UINT virtualKey = MapVirtualKey ((UINT) virtualScanCode, 1);
  2525. UINT keyChar = MapVirtualKey (virtualKey, 2);
  2526. keyChar = LOWORD (keyChar);
  2527. if (keyChar != 0)
  2528. key = (int) keyChar;
  2529. // avoid sending junk text characters for some control-key combinations
  2530. if (textChar < ' ' && ModifierKeys::currentModifiers.testFlags (ModifierKeys::ctrlModifier | ModifierKeys::altModifier))
  2531. textChar = 0;
  2532. }
  2533. return handleKeyPress (key, textChar);
  2534. }
  2535. void forwardMessageToParent (UINT message, WPARAM wParam, LPARAM lParam) const
  2536. {
  2537. if (HWND parentH = GetParent (hwnd))
  2538. PostMessage (parentH, message, wParam, lParam);
  2539. }
  2540. bool doAppCommand (const LPARAM lParam)
  2541. {
  2542. int key = 0;
  2543. switch (GET_APPCOMMAND_LPARAM (lParam))
  2544. {
  2545. case APPCOMMAND_MEDIA_PLAY_PAUSE: key = KeyPress::playKey; break;
  2546. case APPCOMMAND_MEDIA_STOP: key = KeyPress::stopKey; break;
  2547. case APPCOMMAND_MEDIA_NEXTTRACK: key = KeyPress::fastForwardKey; break;
  2548. case APPCOMMAND_MEDIA_PREVIOUSTRACK: key = KeyPress::rewindKey; break;
  2549. default: break;
  2550. }
  2551. if (key != 0)
  2552. {
  2553. updateKeyModifiers();
  2554. if (hwnd == GetActiveWindow())
  2555. {
  2556. handleKeyPress (key, 0);
  2557. return true;
  2558. }
  2559. }
  2560. return false;
  2561. }
  2562. bool isConstrainedNativeWindow() const
  2563. {
  2564. return constrainer != nullptr
  2565. && (styleFlags & (windowHasTitleBar | windowIsResizable)) == (windowHasTitleBar | windowIsResizable)
  2566. && ! isKioskMode();
  2567. }
  2568. Rectangle<int> getCurrentScaledBounds() const
  2569. {
  2570. return ScalingHelpers::unscaledScreenPosToScaled (component, windowBorder.addedTo (ScalingHelpers::scaledScreenPosToUnscaled (component, component.getBounds())));
  2571. }
  2572. LRESULT handleSizeConstraining (RECT& r, const WPARAM wParam)
  2573. {
  2574. if (isConstrainedNativeWindow())
  2575. {
  2576. auto pos = ScalingHelpers::unscaledScreenPosToScaled (component, convertPhysicalScreenRectangleToLogical (rectangleFromRECT (r), hwnd));
  2577. auto current = getCurrentScaledBounds();
  2578. constrainer->checkBounds (pos, current,
  2579. Desktop::getInstance().getDisplays().getTotalBounds (true),
  2580. wParam == WMSZ_TOP || wParam == WMSZ_TOPLEFT || wParam == WMSZ_TOPRIGHT,
  2581. wParam == WMSZ_LEFT || wParam == WMSZ_TOPLEFT || wParam == WMSZ_BOTTOMLEFT,
  2582. wParam == WMSZ_BOTTOM || wParam == WMSZ_BOTTOMLEFT || wParam == WMSZ_BOTTOMRIGHT,
  2583. wParam == WMSZ_RIGHT || wParam == WMSZ_TOPRIGHT || wParam == WMSZ_BOTTOMRIGHT);
  2584. r = RECTFromRectangle (convertLogicalScreenRectangleToPhysical (ScalingHelpers::scaledScreenPosToUnscaled (component, pos), hwnd));
  2585. }
  2586. return TRUE;
  2587. }
  2588. LRESULT handlePositionChanging (WINDOWPOS& wp)
  2589. {
  2590. if (isConstrainedNativeWindow())
  2591. {
  2592. if ((wp.flags & (SWP_NOMOVE | SWP_NOSIZE)) != (SWP_NOMOVE | SWP_NOSIZE)
  2593. && (wp.x > -32000 && wp.y > -32000)
  2594. && ! Component::isMouseButtonDownAnywhere())
  2595. {
  2596. auto pos = ScalingHelpers::unscaledScreenPosToScaled (component, convertPhysicalScreenRectangleToLogical (rectangleFromRECT ({ wp.x, wp.y, wp.x + wp.cx, wp.y + wp.cy }), hwnd));
  2597. auto current = getCurrentScaledBounds();
  2598. constrainer->checkBounds (pos, current,
  2599. Desktop::getInstance().getDisplays().getTotalBounds (true),
  2600. pos.getY() != current.getY() && pos.getBottom() == current.getBottom(),
  2601. pos.getX() != current.getX() && pos.getRight() == current.getRight(),
  2602. pos.getY() == current.getY() && pos.getBottom() != current.getBottom(),
  2603. pos.getX() == current.getX() && pos.getRight() != current.getRight());
  2604. pos = convertLogicalScreenRectangleToPhysical (ScalingHelpers::scaledScreenPosToUnscaled (component, pos), hwnd);
  2605. wp.x = pos.getX();
  2606. wp.y = pos.getY();
  2607. wp.cx = pos.getWidth();
  2608. wp.cy = pos.getHeight();
  2609. }
  2610. }
  2611. if (((wp.flags & SWP_SHOWWINDOW) != 0 && ! component.isVisible()))
  2612. component.setVisible (true);
  2613. else if (((wp.flags & SWP_HIDEWINDOW) != 0 && component.isVisible()))
  2614. component.setVisible (false);
  2615. return 0;
  2616. }
  2617. bool handlePositionChanged()
  2618. {
  2619. auto pos = getCurrentMousePos();
  2620. if (contains (pos.roundToInt(), false))
  2621. {
  2622. if (! areOtherTouchSourcesActive())
  2623. doMouseEvent (pos, MouseInputSource::invalidPressure);
  2624. if (! isValidPeer (this))
  2625. return true;
  2626. }
  2627. handleMovedOrResized();
  2628. return ! dontRepaint; // to allow non-accelerated openGL windows to draw themselves correctly..
  2629. }
  2630. LRESULT handleDPIChanging (int newDPI, RECT newRect)
  2631. {
  2632. auto newScale = (double) newDPI / USER_DEFAULT_SCREEN_DPI;
  2633. if (! approximatelyEqual (scaleFactor, newScale))
  2634. {
  2635. const ScopedValueSetter<bool> setter (isInDPIChange, true);
  2636. auto oldScale = scaleFactor;
  2637. scaleFactor = newScale;
  2638. auto scaleRatio = scaleFactor / oldScale;
  2639. EnumChildWindows (hwnd, scaleChildHWNDCallback, (LPARAM) &scaleRatio);
  2640. setBounds (windowBorder.subtractedFrom (convertPhysicalScreenRectangleToLogical (rectangleFromRECT (newRect), hwnd)), false);
  2641. updateShadower();
  2642. InvalidateRect (hwnd, nullptr, FALSE);
  2643. scaleFactorListeners.call ([&] (ScaleFactorListener& l) { l.nativeScaleFactorChanged (scaleFactor); });
  2644. }
  2645. return 0;
  2646. }
  2647. static BOOL CALLBACK scaleChildHWNDCallback (HWND hwnd, LPARAM context)
  2648. {
  2649. auto r = getWindowRect (hwnd);
  2650. POINT p { r.left, r.top };
  2651. ScreenToClient (GetParent (hwnd), &p);
  2652. auto ratio = *(double*) context;
  2653. SetWindowPos (hwnd, nullptr, roundToInt (p.x * ratio), roundToInt (p.y * ratio),
  2654. roundToInt ((r.right - r.left) * ratio), roundToInt ((r.bottom - r.top) * ratio),
  2655. SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOOWNERZORDER);
  2656. if (auto* peer = getOwnerOfWindow (hwnd))
  2657. peer->handleChildDPIChanging();
  2658. return TRUE;
  2659. }
  2660. void handleChildDPIChanging()
  2661. {
  2662. const ScopedValueSetter<bool> setter (isInDPIChange, true);
  2663. scaleFactor = getScaleFactorForWindow (parentToAddTo);
  2664. updateShadower();
  2665. InvalidateRect (hwnd, nullptr, FALSE);
  2666. scaleFactorListeners.call ([&] (ScaleFactorListener& l) { l.nativeScaleFactorChanged (scaleFactor); });
  2667. }
  2668. void handleAppActivation (const WPARAM wParam)
  2669. {
  2670. modifiersAtLastCallback = -1;
  2671. updateKeyModifiers();
  2672. if (isMinimised())
  2673. {
  2674. component.repaint();
  2675. handleMovedOrResized();
  2676. if (! isValidPeer (this))
  2677. return;
  2678. }
  2679. auto* underMouse = component.getComponentAt (component.getMouseXYRelative());
  2680. if (underMouse == nullptr)
  2681. underMouse = &component;
  2682. if (underMouse->isCurrentlyBlockedByAnotherModalComponent())
  2683. {
  2684. if (LOWORD (wParam) == WA_CLICKACTIVE)
  2685. Component::getCurrentlyModalComponent()->inputAttemptWhenModal();
  2686. else
  2687. ModalComponentManager::getInstance()->bringModalComponentsToFront();
  2688. }
  2689. else
  2690. {
  2691. handleBroughtToFront();
  2692. }
  2693. }
  2694. void handlePowerBroadcast (WPARAM wParam)
  2695. {
  2696. if (auto* app = JUCEApplicationBase::getInstance())
  2697. {
  2698. switch (wParam)
  2699. {
  2700. case PBT_APMSUSPEND: app->suspended(); break;
  2701. case PBT_APMQUERYSUSPENDFAILED:
  2702. case PBT_APMRESUMECRITICAL:
  2703. case PBT_APMRESUMESUSPEND:
  2704. case PBT_APMRESUMEAUTOMATIC: app->resumed(); break;
  2705. default: break;
  2706. }
  2707. }
  2708. }
  2709. void handleLeftClickInNCArea (WPARAM wParam)
  2710. {
  2711. if (! sendInputAttemptWhenModalMessage())
  2712. {
  2713. switch (wParam)
  2714. {
  2715. case HTBOTTOM:
  2716. case HTBOTTOMLEFT:
  2717. case HTBOTTOMRIGHT:
  2718. case HTGROWBOX:
  2719. case HTLEFT:
  2720. case HTRIGHT:
  2721. case HTTOP:
  2722. case HTTOPLEFT:
  2723. case HTTOPRIGHT:
  2724. if (isConstrainedNativeWindow())
  2725. {
  2726. constrainerIsResizing = true;
  2727. constrainer->resizeStart();
  2728. }
  2729. break;
  2730. default:
  2731. break;
  2732. }
  2733. }
  2734. }
  2735. void initialiseSysMenu (HMENU menu) const
  2736. {
  2737. if (! hasTitleBar())
  2738. {
  2739. if (isFullScreen())
  2740. {
  2741. EnableMenuItem (menu, SC_RESTORE, MF_BYCOMMAND | MF_ENABLED);
  2742. EnableMenuItem (menu, SC_MOVE, MF_BYCOMMAND | MF_GRAYED);
  2743. }
  2744. else if (! isMinimised())
  2745. {
  2746. EnableMenuItem (menu, SC_MAXIMIZE, MF_BYCOMMAND | MF_GRAYED);
  2747. }
  2748. }
  2749. }
  2750. void doSettingChange()
  2751. {
  2752. forceDisplayUpdate();
  2753. if (fullScreen && ! isMinimised())
  2754. setWindowPos (hwnd, ScalingHelpers::scaledScreenPosToUnscaled (component, Desktop::getInstance().getDisplays()
  2755. .findDisplayForRect (component.getScreenBounds()).userArea),
  2756. SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOZORDER | SWP_NOSENDCHANGING);
  2757. }
  2758. static void forceDisplayUpdate()
  2759. {
  2760. const_cast<Displays&> (Desktop::getInstance().getDisplays()).refresh();
  2761. }
  2762. //==============================================================================
  2763. #if JUCE_MODULE_AVAILABLE_juce_audio_plugin_client
  2764. void setModifierKeyProvider (ModifierKeyProvider* provider) override
  2765. {
  2766. modProvider = provider;
  2767. }
  2768. void removeModifierKeyProvider() override
  2769. {
  2770. modProvider = nullptr;
  2771. }
  2772. #endif
  2773. //==============================================================================
  2774. public:
  2775. static LRESULT CALLBACK windowProc (HWND h, UINT message, WPARAM wParam, LPARAM lParam)
  2776. {
  2777. // Ensure that non-client areas are scaled for per-monitor DPI awareness v1 - can't
  2778. // do this in peerWindowProc as we have no window at this point
  2779. if (message == WM_NCCREATE && enableNonClientDPIScaling != nullptr)
  2780. enableNonClientDPIScaling (h);
  2781. if (auto* peer = getOwnerOfWindow (h))
  2782. {
  2783. jassert (isValidPeer (peer));
  2784. return peer->peerWindowProc (h, message, wParam, lParam);
  2785. }
  2786. return DefWindowProcW (h, message, wParam, lParam);
  2787. }
  2788. private:
  2789. static void* callFunctionIfNotLocked (MessageCallbackFunction* callback, void* userData)
  2790. {
  2791. auto& mm = *MessageManager::getInstance();
  2792. if (mm.currentThreadHasLockedMessageManager())
  2793. return callback (userData);
  2794. return mm.callFunctionOnMessageThread (callback, userData);
  2795. }
  2796. static POINT getPOINTFromLParam (LPARAM lParam) noexcept
  2797. {
  2798. return { GET_X_LPARAM (lParam), GET_Y_LPARAM (lParam) };
  2799. }
  2800. Point<float> getPointFromLocalLParam (LPARAM lParam) noexcept
  2801. {
  2802. #if JUCE_WIN_PER_MONITOR_DPI_AWARE
  2803. if (isPerMonitorDPIAwareWindow (hwnd))
  2804. {
  2805. // LPARAM is relative to this window's top-left but may be on a different monitor so we need to calculate the
  2806. // physical screen position and then convert this to local logical coordinates
  2807. auto localPos = getPOINTFromLParam (lParam);
  2808. auto r = getWindowRect (hwnd);
  2809. return globalToLocal (Desktop::getInstance().getDisplays().physicalToLogical (pointFromPOINT ({ r.left + localPos.x + roundToInt (windowBorder.getLeft() * scaleFactor),
  2810. r.top + localPos.y + roundToInt (windowBorder.getTop() * scaleFactor) })).toFloat());
  2811. }
  2812. #endif
  2813. return { static_cast<float> (GET_X_LPARAM (lParam)), static_cast<float> (GET_Y_LPARAM (lParam)) };
  2814. }
  2815. Point<float> getCurrentMousePos() noexcept
  2816. {
  2817. return globalToLocal (convertPhysicalScreenPointToLogical (pointFromPOINT (getPOINTFromLParam (GetMessagePos())), hwnd).toFloat());
  2818. }
  2819. LRESULT peerWindowProc (HWND h, UINT message, WPARAM wParam, LPARAM lParam)
  2820. {
  2821. switch (message)
  2822. {
  2823. //==============================================================================
  2824. case WM_NCHITTEST:
  2825. if ((styleFlags & windowIgnoresMouseClicks) != 0)
  2826. return HTTRANSPARENT;
  2827. if (! hasTitleBar())
  2828. return HTCLIENT;
  2829. break;
  2830. //==============================================================================
  2831. case WM_PAINT:
  2832. handlePaintMessage();
  2833. return 0;
  2834. case WM_NCPAINT:
  2835. handlePaintMessage(); // this must be done, even with native titlebars, or there are rendering artifacts.
  2836. if (hasTitleBar())
  2837. break; // let the DefWindowProc handle drawing the frame.
  2838. return 0;
  2839. case WM_ERASEBKGND:
  2840. case WM_NCCALCSIZE:
  2841. if (hasTitleBar())
  2842. break;
  2843. return 1;
  2844. //==============================================================================
  2845. case WM_POINTERUPDATE:
  2846. if (handlePointerInput (wParam, lParam, false, false))
  2847. return 0;
  2848. break;
  2849. case WM_POINTERDOWN:
  2850. if (handlePointerInput (wParam, lParam, true, false))
  2851. return 0;
  2852. break;
  2853. case WM_POINTERUP:
  2854. if (handlePointerInput (wParam, lParam, false, true))
  2855. return 0;
  2856. break;
  2857. //==============================================================================
  2858. case WM_MOUSEMOVE: doMouseMove (getPointFromLocalLParam (lParam), false); return 0;
  2859. case WM_POINTERLEAVE:
  2860. case WM_MOUSELEAVE: doMouseExit(); return 0;
  2861. case WM_LBUTTONDOWN:
  2862. case WM_MBUTTONDOWN:
  2863. case WM_RBUTTONDOWN: doMouseDown (getPointFromLocalLParam (lParam), wParam); return 0;
  2864. case WM_LBUTTONUP:
  2865. case WM_MBUTTONUP:
  2866. case WM_RBUTTONUP: doMouseUp (getPointFromLocalLParam (lParam), wParam); return 0;
  2867. case WM_POINTERWHEEL:
  2868. case 0x020A: /* WM_MOUSEWHEEL */ doMouseWheel (wParam, true); return 0;
  2869. case WM_POINTERHWHEEL:
  2870. case 0x020E: /* WM_MOUSEHWHEEL */ doMouseWheel (wParam, false); return 0;
  2871. case WM_CAPTURECHANGED: doCaptureChanged(); return 0;
  2872. case WM_NCPOINTERUPDATE:
  2873. case WM_NCMOUSEMOVE:
  2874. if (hasTitleBar())
  2875. break;
  2876. return 0;
  2877. case WM_TOUCH:
  2878. if (getTouchInputInfo != nullptr)
  2879. return doTouchEvent ((int) wParam, (HTOUCHINPUT) lParam);
  2880. break;
  2881. case 0x119: /* WM_GESTURE */
  2882. if (doGestureEvent (lParam))
  2883. return 0;
  2884. break;
  2885. //==============================================================================
  2886. case WM_SIZING: return handleSizeConstraining (*(RECT*) lParam, wParam);
  2887. case WM_WINDOWPOSCHANGING: return handlePositionChanging (*(WINDOWPOS*) lParam);
  2888. case 0x2e0: /* WM_DPICHANGED */ return handleDPIChanging ((int) HIWORD (wParam), *(RECT*) lParam);
  2889. case WM_WINDOWPOSCHANGED:
  2890. {
  2891. const WINDOWPOS& wPos = *reinterpret_cast<WINDOWPOS*> (lParam);
  2892. if ((wPos.flags & SWP_NOMOVE) != 0 && (wPos.flags & SWP_NOSIZE) != 0)
  2893. startTimer (100);
  2894. else
  2895. if (handlePositionChanged())
  2896. return 0;
  2897. }
  2898. break;
  2899. //==============================================================================
  2900. case WM_KEYDOWN:
  2901. case WM_SYSKEYDOWN:
  2902. if (doKeyDown (wParam))
  2903. return 0;
  2904. forwardMessageToParent (message, wParam, lParam);
  2905. break;
  2906. case WM_KEYUP:
  2907. case WM_SYSKEYUP:
  2908. if (doKeyUp (wParam))
  2909. return 0;
  2910. forwardMessageToParent (message, wParam, lParam);
  2911. break;
  2912. case WM_CHAR:
  2913. if (doKeyChar ((int) wParam, lParam))
  2914. return 0;
  2915. forwardMessageToParent (message, wParam, lParam);
  2916. break;
  2917. case WM_APPCOMMAND:
  2918. if (doAppCommand (lParam))
  2919. return TRUE;
  2920. break;
  2921. case WM_MENUCHAR: // triggered when alt+something is pressed
  2922. return MNC_CLOSE << 16; // (avoids making the default system beep)
  2923. //==============================================================================
  2924. case WM_SETFOCUS:
  2925. updateKeyModifiers();
  2926. handleFocusGain();
  2927. break;
  2928. case WM_KILLFOCUS:
  2929. if (hasCreatedCaret)
  2930. {
  2931. hasCreatedCaret = false;
  2932. DestroyCaret();
  2933. }
  2934. handleFocusLoss();
  2935. break;
  2936. case WM_ACTIVATEAPP:
  2937. // Windows does weird things to process priority when you swap apps,
  2938. // so this forces an update when the app is brought to the front
  2939. if (wParam != FALSE)
  2940. juce_repeatLastProcessPriority();
  2941. else
  2942. Desktop::getInstance().setKioskModeComponent (nullptr); // turn kiosk mode off if we lose focus
  2943. juce_checkCurrentlyFocusedTopLevelWindow();
  2944. modifiersAtLastCallback = -1;
  2945. return 0;
  2946. case WM_ACTIVATE:
  2947. if (LOWORD (wParam) == WA_ACTIVE || LOWORD (wParam) == WA_CLICKACTIVE)
  2948. {
  2949. handleAppActivation (wParam);
  2950. return 0;
  2951. }
  2952. break;
  2953. case WM_NCACTIVATE:
  2954. // while a temporary window is being shown, prevent Windows from deactivating the
  2955. // title bars of our main windows.
  2956. if (wParam == 0 && ! shouldDeactivateTitleBar)
  2957. wParam = TRUE; // change this and let it get passed to the DefWindowProc.
  2958. break;
  2959. case WM_POINTERACTIVATE:
  2960. case WM_MOUSEACTIVATE:
  2961. if (! component.getMouseClickGrabsKeyboardFocus())
  2962. return MA_NOACTIVATE;
  2963. break;
  2964. case WM_SHOWWINDOW:
  2965. if (wParam != 0)
  2966. {
  2967. component.setVisible (true);
  2968. handleBroughtToFront();
  2969. }
  2970. break;
  2971. case WM_CLOSE:
  2972. if (! component.isCurrentlyBlockedByAnotherModalComponent())
  2973. handleUserClosingWindow();
  2974. return 0;
  2975. #if JUCE_REMOVE_COMPONENT_FROM_DESKTOP_ON_WM_DESTROY
  2976. case WM_DESTROY:
  2977. getComponent().removeFromDesktop();
  2978. return 0;
  2979. #endif
  2980. case WM_QUERYENDSESSION:
  2981. if (auto* app = JUCEApplicationBase::getInstance())
  2982. {
  2983. app->systemRequestedQuit();
  2984. return MessageManager::getInstance()->hasStopMessageBeenSent();
  2985. }
  2986. return TRUE;
  2987. case WM_POWERBROADCAST:
  2988. handlePowerBroadcast (wParam);
  2989. break;
  2990. case WM_SYNCPAINT:
  2991. return 0;
  2992. case WM_DISPLAYCHANGE:
  2993. InvalidateRect (h, nullptr, 0);
  2994. // intentional fall-through...
  2995. JUCE_FALLTHROUGH
  2996. case WM_SETTINGCHANGE: // note the fall-through in the previous case!
  2997. doSettingChange();
  2998. break;
  2999. case WM_INITMENU:
  3000. initialiseSysMenu ((HMENU) wParam);
  3001. break;
  3002. case WM_SYSCOMMAND:
  3003. switch (wParam & 0xfff0)
  3004. {
  3005. case SC_CLOSE:
  3006. if (sendInputAttemptWhenModalMessage())
  3007. return 0;
  3008. if (hasTitleBar())
  3009. {
  3010. PostMessage (h, WM_CLOSE, 0, 0);
  3011. return 0;
  3012. }
  3013. break;
  3014. case SC_KEYMENU:
  3015. #if ! JUCE_WINDOWS_ALT_KEY_TRIGGERS_MENU
  3016. // This test prevents a press of the ALT key from triggering the ancient top-left window menu.
  3017. // By default we suppress this behaviour because it's unlikely that more than a tiny subset of
  3018. // our users will actually want it, and it causes problems if you're trying to use the ALT key
  3019. // as a modifier for mouse actions. If you really need the old behaviour, then just define
  3020. // JUCE_WINDOWS_ALT_KEY_TRIGGERS_MENU=1 in your app.
  3021. if ((lParam >> 16) <= 0) // Values above zero indicate that a mouse-click triggered the menu
  3022. return 0;
  3023. #endif
  3024. // (NB mustn't call sendInputAttemptWhenModalMessage() here because of very obscure
  3025. // situations that can arise if a modal loop is started from an alt-key keypress).
  3026. if (hasTitleBar() && h == GetCapture())
  3027. ReleaseCapture();
  3028. break;
  3029. case SC_MAXIMIZE:
  3030. if (! sendInputAttemptWhenModalMessage())
  3031. setFullScreen (true);
  3032. return 0;
  3033. case SC_MINIMIZE:
  3034. if (sendInputAttemptWhenModalMessage())
  3035. return 0;
  3036. if (! hasTitleBar())
  3037. {
  3038. setMinimised (true);
  3039. return 0;
  3040. }
  3041. break;
  3042. case SC_RESTORE:
  3043. if (sendInputAttemptWhenModalMessage())
  3044. return 0;
  3045. if (hasTitleBar())
  3046. {
  3047. if (isFullScreen())
  3048. {
  3049. setFullScreen (false);
  3050. return 0;
  3051. }
  3052. }
  3053. else
  3054. {
  3055. if (isMinimised())
  3056. setMinimised (false);
  3057. else if (isFullScreen())
  3058. setFullScreen (false);
  3059. return 0;
  3060. }
  3061. break;
  3062. }
  3063. break;
  3064. case WM_NCPOINTERDOWN:
  3065. case WM_NCLBUTTONDOWN:
  3066. handleLeftClickInNCArea (wParam);
  3067. break;
  3068. case WM_NCRBUTTONDOWN:
  3069. case WM_NCMBUTTONDOWN:
  3070. sendInputAttemptWhenModalMessage();
  3071. break;
  3072. case WM_IME_SETCONTEXT:
  3073. imeHandler.handleSetContext (h, wParam == TRUE);
  3074. lParam &= ~ISC_SHOWUICOMPOSITIONWINDOW;
  3075. break;
  3076. case WM_IME_STARTCOMPOSITION: imeHandler.handleStartComposition (*this); return 0;
  3077. case WM_IME_ENDCOMPOSITION: imeHandler.handleEndComposition (*this, h); break;
  3078. case WM_IME_COMPOSITION: imeHandler.handleComposition (*this, h, lParam); return 0;
  3079. case WM_GETDLGCODE:
  3080. return DLGC_WANTALLKEYS;
  3081. default:
  3082. break;
  3083. }
  3084. return DefWindowProcW (h, message, wParam, lParam);
  3085. }
  3086. bool sendInputAttemptWhenModalMessage()
  3087. {
  3088. if (component.isCurrentlyBlockedByAnotherModalComponent())
  3089. {
  3090. if (Component* const current = Component::getCurrentlyModalComponent())
  3091. current->inputAttemptWhenModal();
  3092. return true;
  3093. }
  3094. return false;
  3095. }
  3096. //==============================================================================
  3097. struct IMEHandler
  3098. {
  3099. IMEHandler()
  3100. {
  3101. reset();
  3102. }
  3103. void handleSetContext (HWND hWnd, const bool windowIsActive)
  3104. {
  3105. if (compositionInProgress && ! windowIsActive)
  3106. {
  3107. compositionInProgress = false;
  3108. if (HIMC hImc = ImmGetContext (hWnd))
  3109. {
  3110. ImmNotifyIME (hImc, NI_COMPOSITIONSTR, CPS_COMPLETE, 0);
  3111. ImmReleaseContext (hWnd, hImc);
  3112. }
  3113. }
  3114. }
  3115. void handleStartComposition (ComponentPeer& owner)
  3116. {
  3117. reset();
  3118. if (auto* target = owner.findCurrentTextInputTarget())
  3119. target->insertTextAtCaret (String());
  3120. }
  3121. void handleEndComposition (ComponentPeer& owner, HWND hWnd)
  3122. {
  3123. if (compositionInProgress)
  3124. {
  3125. // If this occurs, the user has cancelled the composition, so clear their changes..
  3126. if (auto* target = owner.findCurrentTextInputTarget())
  3127. {
  3128. target->setHighlightedRegion (compositionRange);
  3129. target->insertTextAtCaret (String());
  3130. compositionRange.setLength (0);
  3131. target->setHighlightedRegion (Range<int>::emptyRange (compositionRange.getEnd()));
  3132. target->setTemporaryUnderlining ({});
  3133. }
  3134. if (auto hImc = ImmGetContext (hWnd))
  3135. {
  3136. ImmNotifyIME (hImc, NI_CLOSECANDIDATE, 0, 0);
  3137. ImmReleaseContext (hWnd, hImc);
  3138. }
  3139. }
  3140. reset();
  3141. }
  3142. void handleComposition (ComponentPeer& owner, HWND hWnd, const LPARAM lParam)
  3143. {
  3144. if (auto* target = owner.findCurrentTextInputTarget())
  3145. {
  3146. if (auto hImc = ImmGetContext (hWnd))
  3147. {
  3148. if (compositionRange.getStart() < 0)
  3149. compositionRange = Range<int>::emptyRange (target->getHighlightedRegion().getStart());
  3150. if ((lParam & GCS_RESULTSTR) != 0) // (composition has finished)
  3151. {
  3152. replaceCurrentSelection (target, getCompositionString (hImc, GCS_RESULTSTR),
  3153. Range<int>::emptyRange (-1));
  3154. reset();
  3155. target->setTemporaryUnderlining ({});
  3156. }
  3157. else if ((lParam & GCS_COMPSTR) != 0) // (composition is still in-progress)
  3158. {
  3159. replaceCurrentSelection (target, getCompositionString (hImc, GCS_COMPSTR),
  3160. getCompositionSelection (hImc, lParam));
  3161. target->setTemporaryUnderlining (getCompositionUnderlines (hImc, lParam));
  3162. compositionInProgress = true;
  3163. }
  3164. moveCandidateWindowToLeftAlignWithSelection (hImc, owner, target);
  3165. ImmReleaseContext (hWnd, hImc);
  3166. }
  3167. }
  3168. }
  3169. private:
  3170. //==============================================================================
  3171. Range<int> compositionRange; // The range being modified in the TextInputTarget
  3172. bool compositionInProgress;
  3173. //==============================================================================
  3174. void reset()
  3175. {
  3176. compositionRange = Range<int>::emptyRange (-1);
  3177. compositionInProgress = false;
  3178. }
  3179. String getCompositionString (HIMC hImc, const DWORD type) const
  3180. {
  3181. jassert (hImc != nullptr);
  3182. const auto stringSizeBytes = ImmGetCompositionString (hImc, type, nullptr, 0);
  3183. if (stringSizeBytes > 0)
  3184. {
  3185. HeapBlock<TCHAR> buffer;
  3186. buffer.calloc ((size_t) stringSizeBytes / sizeof (TCHAR) + 1);
  3187. ImmGetCompositionString (hImc, type, buffer, (DWORD) stringSizeBytes);
  3188. return String (buffer.get());
  3189. }
  3190. return {};
  3191. }
  3192. int getCompositionCaretPos (HIMC hImc, LPARAM lParam, const String& currentIMEString) const
  3193. {
  3194. jassert (hImc != nullptr);
  3195. if ((lParam & CS_NOMOVECARET) != 0)
  3196. return compositionRange.getStart();
  3197. if ((lParam & GCS_CURSORPOS) != 0)
  3198. {
  3199. const int localCaretPos = ImmGetCompositionString (hImc, GCS_CURSORPOS, nullptr, 0);
  3200. return compositionRange.getStart() + jmax (0, localCaretPos);
  3201. }
  3202. return compositionRange.getStart() + currentIMEString.length();
  3203. }
  3204. // Get selected/highlighted range while doing composition:
  3205. // returned range is relative to beginning of TextInputTarget, not composition string
  3206. Range<int> getCompositionSelection (HIMC hImc, LPARAM lParam) const
  3207. {
  3208. jassert (hImc != nullptr);
  3209. int selectionStart = 0;
  3210. int selectionEnd = 0;
  3211. if ((lParam & GCS_COMPATTR) != 0)
  3212. {
  3213. // Get size of attributes array:
  3214. const int attributeSizeBytes = ImmGetCompositionString (hImc, GCS_COMPATTR, nullptr, 0);
  3215. if (attributeSizeBytes > 0)
  3216. {
  3217. // Get attributes (8 bit flag per character):
  3218. HeapBlock<char> attributes (attributeSizeBytes);
  3219. ImmGetCompositionString (hImc, GCS_COMPATTR, attributes, (DWORD) attributeSizeBytes);
  3220. selectionStart = 0;
  3221. for (selectionStart = 0; selectionStart < attributeSizeBytes; ++selectionStart)
  3222. if (attributes[selectionStart] == ATTR_TARGET_CONVERTED || attributes[selectionStart] == ATTR_TARGET_NOTCONVERTED)
  3223. break;
  3224. for (selectionEnd = selectionStart; selectionEnd < attributeSizeBytes; ++selectionEnd)
  3225. if (attributes[selectionEnd] != ATTR_TARGET_CONVERTED && attributes[selectionEnd] != ATTR_TARGET_NOTCONVERTED)
  3226. break;
  3227. }
  3228. }
  3229. return Range<int> (selectionStart, selectionEnd) + compositionRange.getStart();
  3230. }
  3231. void replaceCurrentSelection (TextInputTarget* const target, const String& newContent, Range<int> newSelection)
  3232. {
  3233. if (compositionInProgress)
  3234. target->setHighlightedRegion (compositionRange);
  3235. target->insertTextAtCaret (newContent);
  3236. compositionRange.setLength (newContent.length());
  3237. if (newSelection.getStart() < 0)
  3238. newSelection = Range<int>::emptyRange (compositionRange.getEnd());
  3239. target->setHighlightedRegion (newSelection);
  3240. }
  3241. Array<Range<int>> getCompositionUnderlines (HIMC hImc, LPARAM lParam) const
  3242. {
  3243. Array<Range<int>> result;
  3244. if (hImc != nullptr && (lParam & GCS_COMPCLAUSE) != 0)
  3245. {
  3246. auto clauseDataSizeBytes = ImmGetCompositionString (hImc, GCS_COMPCLAUSE, nullptr, 0);
  3247. if (clauseDataSizeBytes > 0)
  3248. {
  3249. const auto numItems = (size_t) clauseDataSizeBytes / sizeof (uint32);
  3250. HeapBlock<uint32> clauseData (numItems);
  3251. if (ImmGetCompositionString (hImc, GCS_COMPCLAUSE, clauseData, (DWORD) clauseDataSizeBytes) > 0)
  3252. for (size_t i = 0; i + 1 < numItems; ++i)
  3253. result.add (Range<int> ((int) clauseData[i], (int) clauseData[i + 1]) + compositionRange.getStart());
  3254. }
  3255. }
  3256. return result;
  3257. }
  3258. void moveCandidateWindowToLeftAlignWithSelection (HIMC hImc, ComponentPeer& peer, TextInputTarget* target) const
  3259. {
  3260. if (auto* targetComp = dynamic_cast<Component*> (target))
  3261. {
  3262. auto area = peer.getComponent().getLocalArea (targetComp, target->getCaretRectangle());
  3263. CANDIDATEFORM pos = { 0, CFS_CANDIDATEPOS, { area.getX(), area.getBottom() }, { 0, 0, 0, 0 } };
  3264. ImmSetCandidateWindow (hImc, &pos);
  3265. }
  3266. }
  3267. JUCE_DECLARE_NON_COPYABLE (IMEHandler)
  3268. };
  3269. void timerCallback() override
  3270. {
  3271. handlePositionChanged();
  3272. stopTimer();
  3273. }
  3274. IMEHandler imeHandler;
  3275. //==============================================================================
  3276. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (HWNDComponentPeer)
  3277. };
  3278. MultiTouchMapper<DWORD> HWNDComponentPeer::currentTouches;
  3279. ModifierKeys HWNDComponentPeer::modifiersAtLastCallback;
  3280. ComponentPeer* Component::createNewPeer (int styleFlags, void* parentHWND)
  3281. {
  3282. return new HWNDComponentPeer (*this, styleFlags, (HWND) parentHWND, false);
  3283. }
  3284. JUCE_API ComponentPeer* createNonRepaintingEmbeddedWindowsPeer (Component& component, void* parentHWND)
  3285. {
  3286. return new HWNDComponentPeer (component, ComponentPeer::windowIgnoresMouseClicks,
  3287. (HWND) parentHWND, true);
  3288. }
  3289. JUCE_IMPLEMENT_SINGLETON (HWNDComponentPeer::WindowClassHolder)
  3290. //==============================================================================
  3291. bool KeyPress::isKeyCurrentlyDown (const int keyCode)
  3292. {
  3293. auto k = (SHORT) keyCode;
  3294. if ((keyCode & extendedKeyModifier) == 0)
  3295. {
  3296. if (k >= (SHORT) 'a' && k <= (SHORT) 'z')
  3297. k += (SHORT) 'A' - (SHORT) 'a';
  3298. // Only translate if extendedKeyModifier flag is not set
  3299. const SHORT translatedValues[] = { (SHORT) ',', VK_OEM_COMMA,
  3300. (SHORT) '+', VK_OEM_PLUS,
  3301. (SHORT) '-', VK_OEM_MINUS,
  3302. (SHORT) '.', VK_OEM_PERIOD,
  3303. (SHORT) ';', VK_OEM_1,
  3304. (SHORT) ':', VK_OEM_1,
  3305. (SHORT) '/', VK_OEM_2,
  3306. (SHORT) '?', VK_OEM_2,
  3307. (SHORT) '[', VK_OEM_4,
  3308. (SHORT) ']', VK_OEM_6 };
  3309. for (int i = 0; i < numElementsInArray (translatedValues); i += 2)
  3310. if (k == translatedValues[i])
  3311. k = translatedValues[i + 1];
  3312. }
  3313. return HWNDComponentPeer::isKeyDown (k);
  3314. }
  3315. // (This internal function is used by the plugin client module)
  3316. bool offerKeyMessageToJUCEWindow (MSG& m) { return HWNDComponentPeer::offerKeyMessageToJUCEWindow (m); }
  3317. //==============================================================================
  3318. bool JUCE_CALLTYPE Process::isForegroundProcess()
  3319. {
  3320. if (auto fg = GetForegroundWindow())
  3321. {
  3322. DWORD processID = 0;
  3323. GetWindowThreadProcessId (fg, &processID);
  3324. return processID == GetCurrentProcessId();
  3325. }
  3326. return true;
  3327. }
  3328. // N/A on Windows as far as I know.
  3329. void JUCE_CALLTYPE Process::makeForegroundProcess() {}
  3330. void JUCE_CALLTYPE Process::hide() {}
  3331. //==============================================================================
  3332. static BOOL CALLBACK enumAlwaysOnTopWindows (HWND hwnd, LPARAM lParam)
  3333. {
  3334. if (IsWindowVisible (hwnd))
  3335. {
  3336. DWORD processID = 0;
  3337. GetWindowThreadProcessId (hwnd, &processID);
  3338. if (processID == GetCurrentProcessId())
  3339. {
  3340. WINDOWINFO info;
  3341. if (GetWindowInfo (hwnd, &info)
  3342. && (info.dwExStyle & WS_EX_TOPMOST) != 0)
  3343. {
  3344. *reinterpret_cast<bool*> (lParam) = true;
  3345. return FALSE;
  3346. }
  3347. }
  3348. }
  3349. return TRUE;
  3350. }
  3351. bool juce_areThereAnyAlwaysOnTopWindows()
  3352. {
  3353. bool anyAlwaysOnTopFound = false;
  3354. EnumWindows (&enumAlwaysOnTopWindows, (LPARAM) &anyAlwaysOnTopFound);
  3355. return anyAlwaysOnTopFound;
  3356. }
  3357. //==============================================================================
  3358. class WindowsMessageBox : public AsyncUpdater
  3359. {
  3360. public:
  3361. WindowsMessageBox (AlertWindow::AlertIconType iconType,
  3362. const String& boxTitle, const String& m,
  3363. Component* associatedComponent, UINT extraFlags,
  3364. ModalComponentManager::Callback* cb, const bool runAsync)
  3365. : flags (extraFlags | getMessageBoxFlags (iconType)),
  3366. owner (getWindowForMessageBox (associatedComponent)),
  3367. title (boxTitle), message (m), callback (cb)
  3368. {
  3369. if (runAsync)
  3370. triggerAsyncUpdate();
  3371. }
  3372. int getResult() const
  3373. {
  3374. const int r = MessageBox (owner, message.toWideCharPointer(), title.toWideCharPointer(), flags);
  3375. return (r == IDYES || r == IDOK) ? 1 : (r == IDNO && (flags & 1) != 0 ? 2 : 0);
  3376. }
  3377. void handleAsyncUpdate() override
  3378. {
  3379. const int result = getResult();
  3380. if (callback != nullptr)
  3381. callback->modalStateFinished (result);
  3382. delete this;
  3383. }
  3384. private:
  3385. UINT flags;
  3386. HWND owner;
  3387. String title, message;
  3388. std::unique_ptr<ModalComponentManager::Callback> callback;
  3389. static UINT getMessageBoxFlags (AlertWindow::AlertIconType iconType) noexcept
  3390. {
  3391. UINT flags = MB_TASKMODAL | MB_SETFOREGROUND;
  3392. // this window can get lost behind JUCE windows which are set to be alwaysOnTop
  3393. // so if there are any set it to be topmost
  3394. if (juce_areThereAnyAlwaysOnTopWindows())
  3395. flags |= MB_TOPMOST;
  3396. switch (iconType)
  3397. {
  3398. case AlertWindow::QuestionIcon: flags |= MB_ICONQUESTION; break;
  3399. case AlertWindow::WarningIcon: flags |= MB_ICONWARNING; break;
  3400. case AlertWindow::InfoIcon: flags |= MB_ICONINFORMATION; break;
  3401. case AlertWindow::NoIcon: JUCE_FALLTHROUGH
  3402. default: break;
  3403. }
  3404. return flags;
  3405. }
  3406. static HWND getWindowForMessageBox (Component* associatedComponent)
  3407. {
  3408. return associatedComponent != nullptr ? (HWND) associatedComponent->getWindowHandle() : nullptr;
  3409. }
  3410. };
  3411. #if JUCE_MODAL_LOOPS_PERMITTED
  3412. void JUCE_CALLTYPE NativeMessageBox::showMessageBox (AlertWindow::AlertIconType iconType,
  3413. const String& title, const String& message,
  3414. Component* associatedComponent)
  3415. {
  3416. WindowsMessageBox box (iconType, title, message, associatedComponent, MB_OK, nullptr, false);
  3417. (void) box.getResult();
  3418. }
  3419. #endif
  3420. void JUCE_CALLTYPE NativeMessageBox::showMessageBoxAsync (AlertWindow::AlertIconType iconType,
  3421. const String& title, const String& message,
  3422. Component* associatedComponent,
  3423. ModalComponentManager::Callback* callback)
  3424. {
  3425. new WindowsMessageBox (iconType, title, message, associatedComponent, MB_OK, callback, true);
  3426. }
  3427. bool JUCE_CALLTYPE NativeMessageBox::showOkCancelBox (AlertWindow::AlertIconType iconType,
  3428. const String& title, const String& message,
  3429. Component* associatedComponent,
  3430. ModalComponentManager::Callback* callback)
  3431. {
  3432. std::unique_ptr<WindowsMessageBox> mb (new WindowsMessageBox (iconType, title, message, associatedComponent,
  3433. MB_OKCANCEL, callback, callback != nullptr));
  3434. if (callback == nullptr)
  3435. return mb->getResult() != 0;
  3436. mb.release();
  3437. return false;
  3438. }
  3439. int JUCE_CALLTYPE NativeMessageBox::showYesNoCancelBox (AlertWindow::AlertIconType iconType,
  3440. const String& title, const String& message,
  3441. Component* associatedComponent,
  3442. ModalComponentManager::Callback* callback)
  3443. {
  3444. std::unique_ptr<WindowsMessageBox> mb (new WindowsMessageBox (iconType, title, message, associatedComponent,
  3445. MB_YESNOCANCEL, callback, callback != nullptr));
  3446. if (callback == nullptr)
  3447. return mb->getResult();
  3448. mb.release();
  3449. return 0;
  3450. }
  3451. int JUCE_CALLTYPE NativeMessageBox::showYesNoBox (AlertWindow::AlertIconType iconType,
  3452. const String& title, const String& message,
  3453. Component* associatedComponent,
  3454. ModalComponentManager::Callback* callback)
  3455. {
  3456. std::unique_ptr<WindowsMessageBox> mb (new WindowsMessageBox (iconType, title, message, associatedComponent,
  3457. MB_YESNO, callback, callback != nullptr));
  3458. if (callback == nullptr)
  3459. return mb->getResult();
  3460. mb.release();
  3461. return 0;
  3462. }
  3463. //==============================================================================
  3464. bool MouseInputSource::SourceList::addSource()
  3465. {
  3466. auto numSources = sources.size();
  3467. if (numSources == 0 || canUseMultiTouch())
  3468. {
  3469. addSource (numSources, numSources == 0 ? MouseInputSource::InputSourceType::mouse
  3470. : MouseInputSource::InputSourceType::touch);
  3471. return true;
  3472. }
  3473. return false;
  3474. }
  3475. bool MouseInputSource::SourceList::canUseTouch()
  3476. {
  3477. return canUseMultiTouch();
  3478. }
  3479. Point<float> MouseInputSource::getCurrentRawMousePosition()
  3480. {
  3481. POINT mousePos;
  3482. GetCursorPos (&mousePos);
  3483. auto p = pointFromPOINT (mousePos);
  3484. #if JUCE_WIN_PER_MONITOR_DPI_AWARE
  3485. if (isPerMonitorDPIAwareThread())
  3486. p = Desktop::getInstance().getDisplays().physicalToLogical (p);
  3487. #endif
  3488. return p.toFloat();
  3489. }
  3490. void MouseInputSource::setRawMousePosition (Point<float> newPosition)
  3491. {
  3492. auto newPositionInt = newPosition.roundToInt();
  3493. #if JUCE_WIN_PER_MONITOR_DPI_AWARE
  3494. if (isPerMonitorDPIAwareThread())
  3495. newPositionInt = Desktop::getInstance().getDisplays().logicalToPhysical (newPositionInt);
  3496. #endif
  3497. auto point = POINTFromPoint (newPositionInt);
  3498. SetCursorPos (point.x, point.y);
  3499. }
  3500. //==============================================================================
  3501. class ScreenSaverDefeater : public Timer
  3502. {
  3503. public:
  3504. ScreenSaverDefeater()
  3505. {
  3506. startTimer (10000);
  3507. timerCallback();
  3508. }
  3509. void timerCallback() override
  3510. {
  3511. if (Process::isForegroundProcess())
  3512. {
  3513. INPUT input = {};
  3514. input.type = INPUT_MOUSE;
  3515. input.mi.mouseData = MOUSEEVENTF_MOVE;
  3516. SendInput (1, &input, sizeof (INPUT));
  3517. }
  3518. }
  3519. };
  3520. static std::unique_ptr<ScreenSaverDefeater> screenSaverDefeater;
  3521. void Desktop::setScreenSaverEnabled (const bool isEnabled)
  3522. {
  3523. if (isEnabled)
  3524. screenSaverDefeater = nullptr;
  3525. else if (screenSaverDefeater == nullptr)
  3526. screenSaverDefeater.reset (new ScreenSaverDefeater());
  3527. }
  3528. bool Desktop::isScreenSaverEnabled()
  3529. {
  3530. return screenSaverDefeater == nullptr;
  3531. }
  3532. //==============================================================================
  3533. void LookAndFeel::playAlertSound()
  3534. {
  3535. MessageBeep (MB_OK);
  3536. }
  3537. //==============================================================================
  3538. void SystemClipboard::copyTextToClipboard (const String& text)
  3539. {
  3540. if (OpenClipboard (nullptr) != 0)
  3541. {
  3542. if (EmptyClipboard() != 0)
  3543. {
  3544. auto bytesNeeded = CharPointer_UTF16::getBytesRequiredFor (text.getCharPointer()) + 4;
  3545. if (bytesNeeded > 0)
  3546. {
  3547. if (auto bufH = GlobalAlloc (GMEM_MOVEABLE | GMEM_DDESHARE | GMEM_ZEROINIT, bytesNeeded + sizeof (WCHAR)))
  3548. {
  3549. if (auto* data = static_cast<WCHAR*> (GlobalLock (bufH)))
  3550. {
  3551. text.copyToUTF16 (data, bytesNeeded);
  3552. GlobalUnlock (bufH);
  3553. SetClipboardData (CF_UNICODETEXT, bufH);
  3554. }
  3555. }
  3556. }
  3557. }
  3558. CloseClipboard();
  3559. }
  3560. }
  3561. String SystemClipboard::getTextFromClipboard()
  3562. {
  3563. String result;
  3564. if (OpenClipboard (nullptr) != 0)
  3565. {
  3566. if (auto bufH = GetClipboardData (CF_UNICODETEXT))
  3567. {
  3568. if (auto* data = (const WCHAR*) GlobalLock (bufH))
  3569. {
  3570. result = String (data, (size_t) (GlobalSize (bufH) / sizeof (WCHAR)));
  3571. GlobalUnlock (bufH);
  3572. }
  3573. }
  3574. CloseClipboard();
  3575. }
  3576. return result;
  3577. }
  3578. //==============================================================================
  3579. void Desktop::setKioskComponent (Component* kioskModeComp, bool enableOrDisable, bool /*allowMenusAndBars*/)
  3580. {
  3581. if (auto* tlw = dynamic_cast<TopLevelWindow*> (kioskModeComp))
  3582. tlw->setUsingNativeTitleBar (! enableOrDisable);
  3583. if (enableOrDisable)
  3584. kioskModeComp->setBounds (getDisplays().findDisplayForRect (kioskModeComp->getScreenBounds()).totalArea);
  3585. }
  3586. void Desktop::allowedOrientationsChanged() {}
  3587. //==============================================================================
  3588. static const Displays::Display* getCurrentDisplayFromScaleFactor (HWND hwnd)
  3589. {
  3590. Array<const Displays::Display*> candidateDisplays;
  3591. double scaleToLookFor = -1.0;
  3592. if (auto* peer = HWNDComponentPeer::getOwnerOfWindow (hwnd))
  3593. scaleToLookFor = peer->getPlatformScaleFactor();
  3594. else
  3595. scaleToLookFor = getScaleFactorForWindow (hwnd);
  3596. auto globalScale = Desktop::getInstance().getGlobalScaleFactor();
  3597. for (auto& d : Desktop::getInstance().getDisplays().displays)
  3598. if (approximatelyEqual (d.scale / globalScale, scaleToLookFor))
  3599. candidateDisplays.add (&d);
  3600. if (candidateDisplays.size() > 0)
  3601. {
  3602. if (candidateDisplays.size() == 1)
  3603. return candidateDisplays[0];
  3604. Rectangle<int> bounds;
  3605. if (auto* peer = HWNDComponentPeer::getOwnerOfWindow (hwnd))
  3606. bounds = peer->getComponent().getTopLevelComponent()->getBounds();
  3607. else
  3608. bounds = Desktop::getInstance().getDisplays().physicalToLogical (rectangleFromRECT (getWindowRect (hwnd)));
  3609. const Displays::Display* retVal = nullptr;
  3610. int maxArea = -1;
  3611. for (auto* d : candidateDisplays)
  3612. {
  3613. auto intersection = d->totalArea.getIntersection (bounds);
  3614. auto area = intersection.getWidth() * intersection.getHeight();
  3615. if (area > maxArea)
  3616. {
  3617. maxArea = area;
  3618. retVal = d;
  3619. }
  3620. }
  3621. if (retVal != nullptr)
  3622. return retVal;
  3623. }
  3624. return &Desktop::getInstance().getDisplays().getMainDisplay();
  3625. }
  3626. //==============================================================================
  3627. struct MonitorInfo
  3628. {
  3629. MonitorInfo (bool main, RECT rect, double d) noexcept
  3630. : isMain (main), bounds (rect), dpi (d) {}
  3631. bool isMain;
  3632. RECT bounds;
  3633. double dpi;
  3634. };
  3635. static BOOL CALLBACK enumMonitorsProc (HMONITOR hm, HDC, LPRECT r, LPARAM userInfo)
  3636. {
  3637. MONITORINFO info = {};
  3638. info.cbSize = sizeof (info);
  3639. GetMonitorInfo (hm, &info);
  3640. auto isMain = (info.dwFlags & 1 /* MONITORINFOF_PRIMARY */) != 0;
  3641. auto dpi = 0.0;
  3642. if (getDPIForMonitor != nullptr)
  3643. {
  3644. UINT dpiX = 0, dpiY = 0;
  3645. if (SUCCEEDED (getDPIForMonitor (hm, MDT_Default, &dpiX, &dpiY)))
  3646. dpi = (dpiX + dpiY) / 2.0;
  3647. }
  3648. ((Array<MonitorInfo>*) userInfo)->add ({ isMain, *r, dpi });
  3649. return TRUE;
  3650. }
  3651. void Displays::findDisplays (float masterScale)
  3652. {
  3653. setDPIAwareness();
  3654. Array<MonitorInfo> monitors;
  3655. EnumDisplayMonitors (nullptr, nullptr, &enumMonitorsProc, (LPARAM) &monitors);
  3656. auto globalDPI = getGlobalDPI();
  3657. if (monitors.size() == 0)
  3658. monitors.add ({ true, getWindowRect (GetDesktopWindow()), globalDPI });
  3659. // make sure the first in the list is the main monitor
  3660. for (int i = 1; i < monitors.size(); ++i)
  3661. if (monitors.getReference (i).isMain)
  3662. monitors.swap (i, 0);
  3663. for (auto& monitor : monitors)
  3664. {
  3665. Display d;
  3666. d.isMain = monitor.isMain;
  3667. d.dpi = monitor.dpi;
  3668. if (d.dpi == 0)
  3669. {
  3670. d.dpi = globalDPI;
  3671. d.scale = masterScale;
  3672. }
  3673. else
  3674. {
  3675. d.scale = (d.dpi / USER_DEFAULT_SCREEN_DPI) * (masterScale / Desktop::getDefaultMasterScale());
  3676. }
  3677. d.userArea = d.totalArea = Rectangle<int>::leftTopRightBottom (monitor.bounds.left, monitor.bounds.top,
  3678. monitor.bounds.right, monitor.bounds.bottom);
  3679. if (d.isMain)
  3680. {
  3681. RECT workArea;
  3682. SystemParametersInfo (SPI_GETWORKAREA, 0, &workArea, 0);
  3683. d.userArea = d.userArea.getIntersection (Rectangle<int>::leftTopRightBottom (workArea.left, workArea.top,
  3684. workArea.right, workArea.bottom));
  3685. }
  3686. displays.add (d);
  3687. }
  3688. #if JUCE_WIN_PER_MONITOR_DPI_AWARE
  3689. updateToLogical();
  3690. #else
  3691. for (auto& d : displays)
  3692. {
  3693. d.totalArea /= masterScale;
  3694. d.userArea /= masterScale;
  3695. }
  3696. #endif
  3697. }
  3698. //==============================================================================
  3699. static HICON extractFileHICON (const File& file)
  3700. {
  3701. WORD iconNum = 0;
  3702. WCHAR name[MAX_PATH * 2];
  3703. file.getFullPathName().copyToUTF16 (name, sizeof (name));
  3704. return ExtractAssociatedIcon ((HINSTANCE) Process::getCurrentModuleInstanceHandle(),
  3705. name, &iconNum);
  3706. }
  3707. Image juce_createIconForFile (const File& file)
  3708. {
  3709. Image image;
  3710. if (auto icon = extractFileHICON (file))
  3711. {
  3712. image = IconConverters::createImageFromHICON (icon);
  3713. DestroyIcon (icon);
  3714. }
  3715. return image;
  3716. }
  3717. //==============================================================================
  3718. void* CustomMouseCursorInfo::create() const
  3719. {
  3720. const int maxW = GetSystemMetrics (SM_CXCURSOR);
  3721. const int maxH = GetSystemMetrics (SM_CYCURSOR);
  3722. Image im (image);
  3723. int hotspotX = hotspot.x;
  3724. int hotspotY = hotspot.y;
  3725. if (im.getWidth() > maxW || im.getHeight() > maxH)
  3726. {
  3727. im = im.rescaled (maxW, maxH);
  3728. hotspotX = (hotspotX * maxW) / juce::jmax (1, image.getWidth());
  3729. hotspotY = (hotspotY * maxH) / juce::jmax (1, image.getHeight());
  3730. }
  3731. return IconConverters::createHICONFromImage (im, FALSE, hotspotX, hotspotY);
  3732. }
  3733. void MouseCursor::deleteMouseCursor (void* cursorHandle, bool isStandard)
  3734. {
  3735. if (cursorHandle != nullptr && ! isStandard)
  3736. DestroyCursor ((HCURSOR) cursorHandle);
  3737. }
  3738. enum
  3739. {
  3740. hiddenMouseCursorHandle = 32500 // (arbitrary non-zero value to mark this type of cursor)
  3741. };
  3742. void* MouseCursor::createStandardMouseCursor (const MouseCursor::StandardCursorType type)
  3743. {
  3744. LPCTSTR cursorName = IDC_ARROW;
  3745. switch (type)
  3746. {
  3747. case NormalCursor:
  3748. case ParentCursor: break;
  3749. case NoCursor: return (void*) hiddenMouseCursorHandle;
  3750. case WaitCursor: cursorName = IDC_WAIT; break;
  3751. case IBeamCursor: cursorName = IDC_IBEAM; break;
  3752. case PointingHandCursor: cursorName = MAKEINTRESOURCE(32649); break;
  3753. case CrosshairCursor: cursorName = IDC_CROSS; break;
  3754. case LeftRightResizeCursor:
  3755. case LeftEdgeResizeCursor:
  3756. case RightEdgeResizeCursor: cursorName = IDC_SIZEWE; break;
  3757. case UpDownResizeCursor:
  3758. case TopEdgeResizeCursor:
  3759. case BottomEdgeResizeCursor: cursorName = IDC_SIZENS; break;
  3760. case TopLeftCornerResizeCursor:
  3761. case BottomRightCornerResizeCursor: cursorName = IDC_SIZENWSE; break;
  3762. case TopRightCornerResizeCursor:
  3763. case BottomLeftCornerResizeCursor: cursorName = IDC_SIZENESW; break;
  3764. case UpDownLeftRightResizeCursor: cursorName = IDC_SIZEALL; break;
  3765. case DraggingHandCursor:
  3766. {
  3767. static void* dragHandCursor = nullptr;
  3768. if (dragHandCursor == nullptr)
  3769. {
  3770. static const unsigned char dragHandData[] =
  3771. { 71,73,70,56,57,97,16,0,16,0,145,2,0,0,0,0,255,255,255,0,0,0,0,0,0,33,249,4,1,0,0,2,0,44,0,0,0,0,16,0,
  3772. 16,0,0,2,52,148,47,0,200,185,16,130,90,12,74,139,107,84,123,39,132,117,151,116,132,146,248,60,209,138,
  3773. 98,22,203,114,34,236,37,52,77,217,247,154,191,119,110,240,193,128,193,95,163,56,60,234,98,135,2,0,59 };
  3774. dragHandCursor = CustomMouseCursorInfo (ImageFileFormat::loadFrom (dragHandData, sizeof (dragHandData)), { 8, 7 }).create();
  3775. }
  3776. return dragHandCursor;
  3777. }
  3778. case CopyingCursor:
  3779. {
  3780. static void* copyCursor = nullptr;
  3781. if (copyCursor == nullptr)
  3782. {
  3783. static unsigned char copyCursorData[] = {
  3784. 71,73,70,56,57,97,21,0,21,0,145,0,0,0,0,0,255,255,255,0,128,128,255,255,255,33,249,4,1,0,0,3,0,44,0,0,0,0,21,0,
  3785. 21,0,0,2,72,4,134,169,171,16,199,98,11,79,90,71,161,93,56,111,78,133,218,215,137,31,82,154,100,200,86,91,202,142,
  3786. 12,108,212,87,235,174, 15,54,214,126,237,226,37,96,59,141,16,37,18,201,142,157,230,204,51,112,252,114,147,74,83,
  3787. 5,50,68,147,208,217,16,71,149,252,124,5,0,59,0,0
  3788. };
  3789. const int copyCursorSize = 119;
  3790. copyCursor = CustomMouseCursorInfo (ImageFileFormat::loadFrom (copyCursorData, copyCursorSize), { 1, 3 }).create();
  3791. }
  3792. return copyCursor;
  3793. }
  3794. case NumStandardCursorTypes: JUCE_FALLTHROUGH
  3795. default:
  3796. jassertfalse; break;
  3797. }
  3798. if (auto cursorH = LoadCursor (nullptr, cursorName))
  3799. return cursorH;
  3800. return LoadCursor (nullptr, IDC_ARROW);
  3801. }
  3802. //==============================================================================
  3803. void MouseCursor::showInWindow (ComponentPeer*) const
  3804. {
  3805. auto c = (HCURSOR) getHandle();
  3806. if (c == nullptr)
  3807. c = LoadCursor (nullptr, IDC_ARROW);
  3808. else if (c == (HCURSOR) hiddenMouseCursorHandle)
  3809. c = nullptr;
  3810. SetCursor (c);
  3811. }
  3812. JUCE_END_IGNORE_WARNINGS_GCC_LIKE
  3813. } // namespace juce