llwindowwin32.cpp 97 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882388338843885388638873888388938903891389238933894389538963897389838993900
  1. /**
  2. * @file llwindowwin32.cpp
  3. * @brief Platform-dependent implementation of llwindow
  4. *
  5. * $LicenseInfo:firstyear=2001&license=viewergpl$
  6. *
  7. * Copyright (c) 2001-2009, Linden Research, Inc.
  8. *
  9. * Second Life Viewer Source Code
  10. * The source code in this file ("Source Code") is provided by Linden Lab
  11. * to you under the terms of the GNU General Public License, version 2.0
  12. * ("GPL"), unless you have obtained a separate licensing agreement
  13. * ("Other License"), formally executed by you and Linden Lab. Terms of
  14. * the GPL can be found in doc/GPL-license.txt in this distribution, or
  15. * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
  16. *
  17. * There are special exceptions to the terms and conditions of the GPL as
  18. * it is applied to this Source Code. View the full text of the exception
  19. * in the file doc/FLOSS-exception.txt in this software distribution, or
  20. * online at
  21. * http://secondlifegrid.net/programs/open_source/licensing/flossexception
  22. *
  23. * By copying, modifying or distributing this software, you acknowledge
  24. * that you have read and understood your obligations described above,
  25. * and agree to abide by those obligations.
  26. *
  27. * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
  28. * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
  29. * COMPLETENESS OR PERFORMANCE.
  30. * $/LicenseInfo$
  31. */
  32. #include "linden_common.h"
  33. #include "indra_constants.h"
  34. #if LL_WINDOWS
  35. // DO NOT CHANGE THE ORDER WITH OTHER INCLUDES !
  36. #include "llwindowwin32.h"
  37. #include "llkeyboardwin32.h"
  38. #include "llpreeditor.h"
  39. #include "lldir.h"
  40. #include "llfasttimer.h"
  41. #include "llgl.h"
  42. #include "llglslshader.h"
  43. #include "llsdutil.h"
  44. #include "llstring.h"
  45. #include <commdlg.h>
  46. #include <WinUser.h>
  47. #include <mapi.h>
  48. #include <process.h> // For _spawn
  49. #include <shellapi.h>
  50. #include <Imm.h>
  51. // Require DirectInput version 8
  52. #define DIRECTINPUT_VERSION 0x0800
  53. #ifndef WM_DPICHANGED
  54. # define WM_DPICHANGED 0x02E0
  55. #endif
  56. #ifndef USER_DEFAULT_SCREEN_DPI
  57. # define USER_DEFAULT_SCREEN_DPI 96
  58. #endif
  59. #include <dinput.h>
  60. #include <Dbt.h.>
  61. // Expose desired use of high-performance graphics processor to Optimus driver
  62. extern "C"
  63. {
  64. __declspec(dllexport) DWORD NvOptimusEnablement = 0x00000001;
  65. }
  66. // *HACK: allow the user to ignore HiDPI WM events (for resetting the GPU after
  67. // wiewer exit, in case of black screen).
  68. bool gIgnoreHiDPIEvents = false;
  69. // Culled from winuser.h
  70. #ifndef WM_MOUSEWHEEL /* Added to be compatible with later SDK's */
  71. constexpr S32 WM_MOUSEWHEEL = 0x020A;
  72. #endif
  73. #ifndef WHEEL_DELTA /* Added to be compatible with later SDK's */
  74. constexpr S32 WHEEL_DELTA = 120; /* Value for rolling one detent */
  75. #endif
  76. constexpr S32 MAX_MESSAGE_PER_UPDATE = 20;
  77. constexpr S32 BITS_PER_PIXEL = 32;
  78. constexpr S32 MAX_NUM_RESOLUTIONS = 32;
  79. constexpr F32 ICON_FLASH_TIME = 0.5f;
  80. LPWSTR gIconResource = IDI_APPLICATION;
  81. LLW32MsgCallback gAsyncMsgCallback = NULL;
  82. #ifndef DPI_ENUMS_DECLARED
  83. typedef enum PROCESS_DPI_AWARENESS
  84. {
  85. PROCESS_DPI_UNAWARE = 0,
  86. PROCESS_SYSTEM_DPI_AWARE = 1,
  87. PROCESS_PER_MONITOR_DPI_AWARE = 2
  88. } PROCESS_DPI_AWARENESS;
  89. typedef enum MONITOR_DPI_TYPE
  90. {
  91. MDT_EFFECTIVE_DPI = 0,
  92. MDT_ANGULAR_DPI = 1,
  93. MDT_RAW_DPI = 2,
  94. MDT_DEFAULT = MDT_EFFECTIVE_DPI
  95. } MONITOR_DPI_TYPE;
  96. #endif
  97. typedef HRESULT(STDAPICALLTYPE* SetProcessDpiAwarenessType)(_In_ PROCESS_DPI_AWARENESS value);
  98. typedef HRESULT(STDAPICALLTYPE* GetProcessDpiAwarenessType)(_In_ HANDLE hprocess,
  99. _Out_ PROCESS_DPI_AWARENESS* value);
  100. typedef HRESULT(STDAPICALLTYPE* GetDpiForMonitorType)(_In_ HMONITOR hmonitor,
  101. _In_ MONITOR_DPI_TYPE type,
  102. _Out_ UINT* dpiX,
  103. _Out_ UINT* dpiY);
  104. //
  105. // LLWindowWin32
  106. //
  107. void show_window_creation_error(const std::string& title)
  108. {
  109. llwarns << title << llendl;
  110. }
  111. HGLRC SafeCreateContext(HDC& hdc)
  112. {
  113. __try
  114. {
  115. return wglCreateContext(hdc);
  116. }
  117. __except (EXCEPTION_EXECUTE_HANDLER)
  118. {
  119. return NULL;
  120. }
  121. }
  122. GLuint SafeChoosePixelFormat(HDC& hdc, const PIXELFORMATDESCRIPTOR* ppfd)
  123. {
  124. __try
  125. {
  126. return ChoosePixelFormat(hdc, ppfd);
  127. }
  128. __except (EXCEPTION_EXECUTE_HANDLER)
  129. {
  130. // Convert to C++ styled exception. C exception do not allow classes,
  131. // so it is a regular char array.
  132. char integer_string[32];
  133. sprintf(integer_string, "SEH, code: %lu\n", GetExceptionCode());
  134. throw std::exception(integer_string);
  135. }
  136. }
  137. //static
  138. bool LLWindowWin32::sIsClassRegistered = false;
  139. bool LLWindowWin32::sLanguageTextInputAllowed = true;
  140. bool LLWindowWin32::sWinIMEOpened = false;
  141. HKL LLWindowWin32::sWinInputLocale = 0;
  142. DWORD LLWindowWin32::sWinIMEConversionMode = IME_CMODE_NATIVE;
  143. DWORD LLWindowWin32::sWinIMESentenceMode = IME_SMODE_AUTOMATIC;
  144. LLCoordWindow LLWindowWin32::sWinIMEWindowPosition(-1, -1);
  145. LLWindowWin32::LLWindowWin32(const std::string& title, S32 x, S32 y, U32 width,
  146. U32 height, U32 flags, bool fullscreen,
  147. bool disable_vsync, U32 fsaa_samples)
  148. : LLWindow(fullscreen, flags),
  149. mWindowHandle(NULL),
  150. mhDC(NULL),
  151. mhRC(NULL),
  152. mFSAASamples(fsaa_samples),
  153. mIconResource(gIconResource),
  154. mNativeAspectRatio(0.f),
  155. mMousePositionModified(false),
  156. mInputProcessingPaused(false),
  157. mCustomGammaSet(false),
  158. mPreeditor(NULL),
  159. mKeyCharCode(0),
  160. mKeyScanCode(0),
  161. mKeyVirtualKey(0),
  162. mRawMsg(0),
  163. mRawWParam(0),
  164. mRawLParam(0)
  165. {
  166. // MAINT-516: force-load opengl32.dll just in case windows went sideways
  167. LoadLibrary(L"opengl32.dll");
  168. memset(mCurrentGammaRamp, 0, sizeof(mCurrentGammaRamp));
  169. memset(mPrevGammaRamp, 0, sizeof(mPrevGammaRamp));
  170. if (!SystemParametersInfo(SPI_GETMOUSEVANISH, 0, &mMouseVanish, 0))
  171. {
  172. mMouseVanish = true;
  173. }
  174. // Initialize the keyboard
  175. gKeyboardp = new LLKeyboardWin32();
  176. // Initialize (boot strap) the language text input management, based on the
  177. // system (or user's) default settings.
  178. allowLanguageTextInput(mPreeditor, false);
  179. // Set the window title
  180. if (title.empty())
  181. {
  182. mWindowTitle = new WCHAR[50];
  183. wsprintf(mWindowTitle, L"OpenGL Window");
  184. }
  185. else
  186. {
  187. mWindowTitle = new WCHAR[256]; // Assume title length < 255 chars.
  188. mbstowcs(mWindowTitle, title.c_str(), 255);
  189. mWindowTitle[255] = 0;
  190. }
  191. // Set the window class name to "Second Life" so that it will always be
  192. // found when being sent an SLURL by any other viewer instance wia
  193. // LLAppViewerWin32::sendURLToOtherInstance()
  194. mWindowClassName = new WCHAR[256];
  195. mWindowClassName[255] = 0;
  196. wsprintf(mWindowClassName, L"Second Life");
  197. // We are not clipping yet
  198. SetRect(&mOldMouseClip, 0, 0, 0, 0);
  199. // Make an instance of our window then define the window class
  200. mhInstance = GetModuleHandle(NULL);
  201. mSwapMethod = SWAP_METHOD_UNDEFINED;
  202. // No WPARAM yet.
  203. mLastSizeWParam = 0;
  204. // Windows GDI rects do not include rightmost pixel
  205. RECT window_rect;
  206. window_rect.left = 0L;
  207. window_rect.right = (long)width;
  208. window_rect.top = 0L;
  209. window_rect.bottom = (long)height;
  210. // Grab screen size to sanitize the window
  211. S32 window_border_y = GetSystemMetrics(SM_CYBORDER);
  212. S32 virtual_screen_x = GetSystemMetrics(SM_XVIRTUALSCREEN);
  213. S32 virtual_screen_y = GetSystemMetrics(SM_YVIRTUALSCREEN);
  214. S32 virtual_screen_width = GetSystemMetrics(SM_CXVIRTUALSCREEN);
  215. S32 virtual_screen_height = GetSystemMetrics(SM_CYVIRTUALSCREEN);
  216. if (x < virtual_screen_x)
  217. {
  218. x = virtual_screen_x;
  219. }
  220. if (y < virtual_screen_y - window_border_y)
  221. {
  222. y = virtual_screen_y - window_border_y;
  223. }
  224. if (x + width > virtual_screen_x + virtual_screen_width)
  225. {
  226. x = virtual_screen_x + virtual_screen_width - width;
  227. }
  228. if (y + height > virtual_screen_y + virtual_screen_height)
  229. {
  230. y = virtual_screen_y + virtual_screen_height - height;
  231. }
  232. WNDCLASS wc;
  233. if (!sIsClassRegistered)
  234. {
  235. // Force redraw when resized and create a private device context
  236. // Makes double click messages.
  237. wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC | CS_DBLCLKS;
  238. // Set message handler function
  239. wc.lpfnWndProc = (WNDPROC)mainWindowProc;
  240. // Unused
  241. wc.cbClsExtra = 0;
  242. wc.cbWndExtra = 0;
  243. wc.hInstance = mhInstance;
  244. wc.hIcon = LoadIcon(mhInstance, mIconResource);
  245. // We will set the cursor ourselves
  246. wc.hCursor = NULL;
  247. // Use a black background. HB
  248. wc.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
  249. // We do not use windows menus
  250. wc.lpszMenuName = NULL;
  251. wc.lpszClassName = mWindowClassName;
  252. if (!RegisterClass(&wc))
  253. {
  254. OSMessageBox("RegisterClass failed", "Error", OSMB_OK);
  255. return;
  256. }
  257. sIsClassRegistered = true;
  258. }
  259. // Get the current refresh rate
  260. DEVMODE dev_mode;
  261. ::ZeroMemory(&dev_mode, sizeof(DEVMODE));
  262. dev_mode.dmSize = sizeof(DEVMODE);
  263. DWORD current_refresh;
  264. if (EnumDisplaySettings(NULL, ENUM_CURRENT_SETTINGS, &dev_mode))
  265. {
  266. current_refresh = dev_mode.dmDisplayFrequency;
  267. mNativeAspectRatio = (F32)dev_mode.dmPelsWidth /
  268. (F32)dev_mode.dmPelsHeight;
  269. }
  270. else
  271. {
  272. current_refresh = 60;
  273. }
  274. // Drop resolution and go fullscreen. Use a display mode with our desired
  275. // size and depth, with a refresh rate as close at possible to the user's
  276. // default
  277. if (mFullscreen)
  278. {
  279. bool success = false;
  280. DWORD closest_refresh = 0;
  281. S32 mode_num = 0;
  282. while (EnumDisplaySettings(NULL, mode_num++, &dev_mode))
  283. {
  284. if (dev_mode.dmPelsWidth == width &&
  285. dev_mode.dmPelsHeight == height &&
  286. dev_mode.dmBitsPerPel == BITS_PER_PIXEL)
  287. {
  288. success = true;
  289. if (closest_refresh == 0 ||
  290. dev_mode.dmDisplayFrequency - current_refresh
  291. < closest_refresh - current_refresh)
  292. {
  293. closest_refresh = dev_mode.dmDisplayFrequency;
  294. }
  295. }
  296. }
  297. if (!success)
  298. {
  299. llwarns << "Could not find display mode " << width << " by "
  300. << height << " at " << BITS_PER_PIXEL << " bits per pixel"
  301. << llendl;
  302. if (!EnumDisplaySettings(NULL, ENUM_CURRENT_SETTINGS, &dev_mode))
  303. {
  304. success = false;
  305. }
  306. else if (dev_mode.dmBitsPerPel == BITS_PER_PIXEL)
  307. {
  308. window_rect.right = width = dev_mode.dmPelsWidth;
  309. window_rect.bottom = height = dev_mode.dmPelsHeight;
  310. llwarns << "Current BBP is OK falling back to: " << width
  311. << "x" << height << llendl;
  312. success = true;
  313. }
  314. else
  315. {
  316. llwarns << "Current BBP is BAD: " << dev_mode.dmBitsPerPel
  317. << llendl;
  318. success = false;
  319. }
  320. }
  321. // If we found a good resolution, use it.
  322. if (success)
  323. {
  324. success = setDisplayResolution(width, height, BITS_PER_PIXEL,
  325. closest_refresh);
  326. }
  327. // Keep a copy of the actual current device mode in case we minimize
  328. // and change the screen resolution. JC
  329. EnumDisplaySettings(NULL, ENUM_CURRENT_SETTINGS, &dev_mode);
  330. // If it failed, we do not want to run fullscreen
  331. if (success)
  332. {
  333. mFullscreen = true;
  334. mFullscreenWidth = dev_mode.dmPelsWidth;
  335. mFullscreenHeight = dev_mode.dmPelsHeight;
  336. mFullscreenBits = dev_mode.dmBitsPerPel;
  337. mFullscreenRefresh = dev_mode.dmDisplayFrequency;
  338. llinfos << "Running at " << dev_mode.dmPelsWidth << "x"
  339. << dev_mode.dmPelsHeight << "x" << dev_mode.dmBitsPerPel
  340. << " @ " << dev_mode.dmDisplayFrequency << llendl;
  341. }
  342. else
  343. {
  344. mFullscreen = false;
  345. mFullscreenWidth = -1;
  346. mFullscreenHeight = -1;
  347. mFullscreenBits = -1;
  348. mFullscreenRefresh = -1;
  349. std::string error =
  350. llformat("Unable to run fullscreen at %d x %d.\nRunning in window.",
  351. width, height);
  352. OSMessageBox(error, "Error", OSMB_OK);
  353. }
  354. }
  355. #if 0 // *TODO: add this after resolving _WIN32_WINNT issue
  356. if (!fullscreen)
  357. {
  358. TRACKMOUSEEVENT track_mouse_event;
  359. track_mouse_event.cbSize = sizeof(TRACKMOUSEEVENT);
  360. track_mouse_event.dwFlags = TME_LEAVE;
  361. track_mouse_event.hwndTrack = mWindowHandle;
  362. track_mouse_event.dwHoverTime = HOVER_DEFAULT;
  363. TrackMouseEvent(&track_mouse_event);
  364. }
  365. #endif
  366. // Create GL drawing context
  367. LLCoordScreen win_pos(x, y);
  368. LLCoordScreen win_size(window_rect.right - window_rect.left,
  369. window_rect.bottom - window_rect.top);
  370. if (!switchContext(mFullscreen, win_size, disable_vsync, &win_pos))
  371. {
  372. return;
  373. }
  374. // Start with arrow cursor
  375. initCursors();
  376. setCursor(UI_CURSOR_ARROW);
  377. // Initialize (boot strap) the Language text input management, based on the
  378. // system (or user's) default settings.
  379. allowLanguageTextInput(NULL, false);
  380. }
  381. LLWindowWin32::~LLWindowWin32()
  382. {
  383. delete[] mWindowTitle;
  384. mWindowTitle = NULL;
  385. delete[] mSupportedResolutions;
  386. mSupportedResolutions = NULL;
  387. delete[] mWindowClassName;
  388. mWindowClassName = NULL;
  389. }
  390. void LLWindowWin32::setWindowTitle(const std::string& title)
  391. {
  392. // Remember the new title, for when we switch context
  393. delete mWindowTitle;
  394. mWindowTitle = new WCHAR[256]; // Assume title length < 255 chars.
  395. mbstowcs(mWindowTitle, title.c_str(), 255);
  396. mWindowTitle[255] = 0;
  397. int len = title.size() + 1;
  398. wchar_t* wText = new wchar_t[len];
  399. if (wText)
  400. {
  401. memset(wText, 0, len);
  402. MultiByteToWideChar(CP_ACP, NULL, title.c_str(), -1, wText, len);
  403. SetWindowText(mWindowHandle, wText);
  404. delete[] wText;
  405. }
  406. }
  407. void LLWindowWin32::show()
  408. {
  409. ShowWindow(mWindowHandle, SW_SHOW);
  410. SetForegroundWindow(mWindowHandle);
  411. SetFocus(mWindowHandle);
  412. }
  413. void LLWindowWin32::hide()
  414. {
  415. setMouseClipping(false);
  416. ShowWindow(mWindowHandle, SW_HIDE);
  417. }
  418. void LLWindowWin32::minimize()
  419. {
  420. setMouseClipping(false);
  421. showCursor();
  422. ShowWindow(mWindowHandle, SW_MINIMIZE);
  423. }
  424. void LLWindowWin32::restore()
  425. {
  426. ShowWindow(mWindowHandle, SW_RESTORE);
  427. SetForegroundWindow(mWindowHandle);
  428. SetFocus(mWindowHandle);
  429. }
  430. // According to callstack "c0000005 Access violation" happened inside __try
  431. // block, deep in DestroyWindow and crashed viewer, which should not be
  432. // possible. Manually causing this exception was caught without issues, so
  433. // turning off optimizations for this part to be sure code executes as intended
  434. // (no idea why else __try can get overruled).
  435. #pragma optimize("", off)
  436. bool destroy_window_handler(HWND& hwnd)
  437. {
  438. bool res = true;
  439. __try
  440. {
  441. if (hwnd)
  442. {
  443. res = DestroyWindow(hwnd);
  444. }
  445. }
  446. __except (EXCEPTION_EXECUTE_HANDLER)
  447. {
  448. res = false;
  449. }
  450. return res;
  451. }
  452. #pragma optimize("", on)
  453. // close() destroys all OS-specific code associated with a window.
  454. // Usually called from LLWindow::destroyWindow()
  455. void LLWindowWin32::close()
  456. {
  457. // Is window is already closed ?
  458. if (!mWindowHandle)
  459. {
  460. return;
  461. }
  462. LL_DEBUGS("Window") << "Closing window..." << LL_ENDL;
  463. // Go back to screen mode written in the registry.
  464. if (mFullscreen)
  465. {
  466. minimize();
  467. resetDisplayResolution();
  468. }
  469. #if 1
  470. // Do not process events in our mainWindowProc any longer.
  471. LL_DEBUGS("Window") << "Stopping WM events processing." << LL_ENDL;
  472. SetWindowLongPtr(mWindowHandle, GWLP_USERDATA, NULL);
  473. #endif
  474. // Make sure cursor is visible and we have not mangled the clipping state.
  475. showCursor();
  476. // Make sure cursor is visible and we have not mangled the clipping state.
  477. setMouseClipping(false);
  478. if (gKeyboardp)
  479. {
  480. gKeyboardp->resetKeys();
  481. }
  482. // Clean up remaining GL state
  483. if (gGLManager.mInited)
  484. {
  485. LL_DEBUGS("Window") << "Shutting down GL" << LL_ENDL;
  486. gGLManager.shutdownGL();
  487. }
  488. LL_DEBUGS("Window") << "Releasing Context" << LL_ENDL;
  489. if (mhRC)
  490. {
  491. if (!wglMakeCurrent(NULL, NULL))
  492. {
  493. llwarns << "Release of DC and RC failed" << llendl;
  494. }
  495. if (!wglDeleteContext(mhRC))
  496. {
  497. llwarns << "Release of rendering context failed" << llendl;
  498. }
  499. mhRC = NULL;
  500. }
  501. // Restore gamma to the system values.
  502. restoreGamma();
  503. if (mhDC)
  504. {
  505. if (!ReleaseDC(mWindowHandle, mhDC))
  506. {
  507. llwarns << "Release of ghDC failed" << llendl;
  508. }
  509. mhDC = NULL;
  510. }
  511. LL_DEBUGS("Window") << "Destroying Window" << LL_ENDL;
  512. if (IsWindow(mWindowHandle))
  513. {
  514. // Make sure we do not leave a blank toolbar button.
  515. ShowWindow(mWindowHandle, SW_HIDE);
  516. // This causes WM_DESTROY to be sent *immediately*
  517. if (!destroy_window_handler(mWindowHandle))
  518. {
  519. OSMessageBox("DestroyWindow(mWindowHandle) failed",
  520. "Shutdown Error", OSMB_OK);
  521. }
  522. }
  523. else
  524. {
  525. // Something killed the window while we were busy destroying GL or the
  526. // handle somehow got broken.
  527. llwarns << "Failed to destroy Window, invalid handle !" << llendl;
  528. }
  529. mWindowHandle = NULL;
  530. }
  531. bool LLWindowWin32::getVisible()
  532. {
  533. return mWindowHandle && IsWindowVisible(mWindowHandle);
  534. }
  535. bool LLWindowWin32::getMinimized()
  536. {
  537. return mWindowHandle && IsIconic(mWindowHandle);
  538. }
  539. bool LLWindowWin32::getMaximized()
  540. {
  541. return mWindowHandle && IsZoomed(mWindowHandle);
  542. }
  543. bool LLWindowWin32::maximize()
  544. {
  545. if (!mWindowHandle) return false;
  546. WINDOWPLACEMENT placement;
  547. placement.length = sizeof(WINDOWPLACEMENT);
  548. bool success = GetWindowPlacement(mWindowHandle, &placement);
  549. if (success)
  550. {
  551. placement.showCmd = SW_MAXIMIZE;
  552. success = SetWindowPlacement(mWindowHandle, &placement);
  553. }
  554. return success;
  555. }
  556. bool LLWindowWin32::getPosition(LLCoordScreen* position)
  557. {
  558. RECT window_rect;
  559. if (!position || !mWindowHandle ||
  560. !GetWindowRect(mWindowHandle, &window_rect))
  561. {
  562. return false;
  563. }
  564. position->mX = window_rect.left;
  565. position->mY = window_rect.top;
  566. return true;
  567. }
  568. bool LLWindowWin32::getSize(LLCoordScreen* size)
  569. {
  570. RECT window_rect;
  571. if (!size || !mWindowHandle ||
  572. !GetWindowRect(mWindowHandle, &window_rect))
  573. {
  574. return false;
  575. }
  576. size->mX = window_rect.right - window_rect.left;
  577. size->mY = window_rect.bottom - window_rect.top;
  578. return true;
  579. }
  580. bool LLWindowWin32::getSize(LLCoordWindow* size)
  581. {
  582. RECT client_rect;
  583. if (!size || !mWindowHandle ||
  584. !GetClientRect(mWindowHandle, &client_rect))
  585. {
  586. return false;
  587. }
  588. size->mX = client_rect.right - client_rect.left;
  589. size->mY = client_rect.bottom - client_rect.top;
  590. return true;
  591. }
  592. bool LLWindowWin32::setPosition(const LLCoordScreen position)
  593. {
  594. if (mWindowHandle)
  595. {
  596. LLCoordScreen size;
  597. getSize(&size);
  598. moveWindow(position, size);
  599. return true;
  600. }
  601. return false;
  602. }
  603. bool LLWindowWin32::setSize(const LLCoordScreen size)
  604. {
  605. if (mWindowHandle)
  606. {
  607. LLCoordScreen position;
  608. getPosition(&position);
  609. moveWindow(position, size);
  610. return true;
  611. }
  612. return false;
  613. }
  614. // Changing fullscreen resolution
  615. bool LLWindowWin32::switchContext(bool fullscreen, const LLCoordScreen& size,
  616. bool disable_vsync,
  617. const LLCoordScreen* const posp)
  618. {
  619. GLuint pixel_format;
  620. DEVMODE dev_mode;
  621. ::ZeroMemory(&dev_mode, sizeof(DEVMODE));
  622. dev_mode.dmSize = sizeof(DEVMODE);
  623. DWORD current_refresh, dw_ex_style, dw_style;
  624. RECT window_rect = { 0, 0, 0, 0 };
  625. S32 width = size.mX;
  626. S32 height = size.mY;
  627. bool auto_show = false;
  628. if (mhRC)
  629. {
  630. auto_show = true;
  631. resetDisplayResolution();
  632. }
  633. if (EnumDisplaySettings(NULL, ENUM_CURRENT_SETTINGS, &dev_mode))
  634. {
  635. current_refresh = dev_mode.dmDisplayFrequency;
  636. }
  637. else
  638. {
  639. current_refresh = 60;
  640. }
  641. gGLManager.shutdownGL();
  642. // Destroy GL context
  643. if (mhRC)
  644. {
  645. if (!wglMakeCurrent(NULL, NULL))
  646. {
  647. llwarns << "Release of DC and RC failed" << llendl;
  648. }
  649. if (!wglDeleteContext(mhRC))
  650. {
  651. llwarns << "Release of rendering context failed" << llendl;
  652. }
  653. mhRC = NULL;
  654. }
  655. if (fullscreen)
  656. {
  657. mFullscreen = true;
  658. bool success = false;
  659. DWORD closest_refresh = 0;
  660. S32 mode_num = 0;
  661. while (EnumDisplaySettings(NULL, mode_num++, &dev_mode))
  662. {
  663. if (dev_mode.dmPelsWidth == width &&
  664. dev_mode.dmPelsHeight == height &&
  665. dev_mode.dmBitsPerPel == BITS_PER_PIXEL)
  666. {
  667. success = true;
  668. if (closest_refresh == 0 ||
  669. dev_mode.dmDisplayFrequency - current_refresh <
  670. closest_refresh - current_refresh)
  671. {
  672. closest_refresh = dev_mode.dmDisplayFrequency;
  673. }
  674. }
  675. }
  676. if (!success)
  677. {
  678. llwarns << "Could not find display mode " << width << " by "
  679. << height << " at " << BITS_PER_PIXEL << " bits per pixel"
  680. << llendl;
  681. return false;
  682. }
  683. // If we found a good resolution, use it.
  684. success = setDisplayResolution(width, height, BITS_PER_PIXEL,
  685. closest_refresh);
  686. // Keep a copy of the actual current device mode in case we minimize
  687. // and change the screen resolution. JC
  688. EnumDisplaySettings(NULL, ENUM_CURRENT_SETTINGS, &dev_mode);
  689. if (success)
  690. {
  691. mFullscreen = true;
  692. mFullscreenWidth = dev_mode.dmPelsWidth;
  693. mFullscreenHeight = dev_mode.dmPelsHeight;
  694. mFullscreenBits = dev_mode.dmBitsPerPel;
  695. mFullscreenRefresh = dev_mode.dmDisplayFrequency;
  696. llinfos << "Running at " << dev_mode.dmPelsWidth << "x"
  697. << dev_mode.dmPelsHeight << "x" << dev_mode.dmBitsPerPel
  698. << " @ " << dev_mode.dmDisplayFrequency << llendl;
  699. window_rect.left = 0L;
  700. // Windows GDI rects do not include rightmost pixel:
  701. window_rect.right = (long)width;
  702. window_rect.top = 0L;
  703. window_rect.bottom = (long)height;
  704. dw_ex_style = WS_EX_APPWINDOW;
  705. dw_style = WS_POPUP;
  706. // Move window borders out not to cover window contents
  707. AdjustWindowRectEx(&window_rect, dw_style, FALSE, dw_ex_style);
  708. }
  709. // If it failed, we do not want to run fullscreen
  710. else
  711. {
  712. mFullscreen = false;
  713. mFullscreenWidth = -1;
  714. mFullscreenHeight = -1;
  715. mFullscreenBits = -1;
  716. mFullscreenRefresh = -1;
  717. llinfos << "Unable to run fullscreen at " << width << "x" << height
  718. << llendl;
  719. return false;
  720. }
  721. }
  722. else
  723. {
  724. mFullscreen = false;
  725. window_rect.left = (long)(posp ? posp->mX : 0);
  726. // Windows GDI rects do not include rightmost pixel:
  727. window_rect.right = (long)width + window_rect.left;
  728. window_rect.top = (long)(posp ? posp->mY : 0);
  729. window_rect.bottom = (long)height + window_rect.top;
  730. // Window with an edge
  731. dw_ex_style = WS_EX_APPWINDOW | WS_EX_WINDOWEDGE;
  732. dw_style = WS_OVERLAPPEDWINDOW;
  733. }
  734. // Do not post quit messages when destroying old windows
  735. mPostQuit = false;
  736. if (mWindowHandle && !destroy_window_handler(mWindowHandle))
  737. {
  738. llwarns << "Failed to properly close window before recreating it"
  739. << llendl;
  740. }
  741. // Create window
  742. destroy_window_handler(mWindowHandle);
  743. mWindowHandle =
  744. CreateWindowEx(dw_ex_style, mWindowClassName, mWindowTitle,
  745. WS_CLIPSIBLINGS | WS_CLIPCHILDREN | dw_style,
  746. window_rect.left, // x pos
  747. window_rect.top, // y pos
  748. window_rect.right - window_rect.left, // width
  749. window_rect.bottom - window_rect.top, // height
  750. NULL, NULL, mhInstance, NULL);
  751. if (mWindowHandle)
  752. {
  753. llinfos << "Window has been created." << llendl;
  754. }
  755. else
  756. {
  757. llwarns << "Failed to create window. Error code: " << GetLastError()
  758. << llendl;
  759. }
  760. //-------------------------------------------------------------------------
  761. // Create GL drawing context
  762. //-------------------------------------------------------------------------
  763. static PIXELFORMATDESCRIPTOR pfd =
  764. {
  765. sizeof(PIXELFORMATDESCRIPTOR),
  766. 1,
  767. PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER,
  768. PFD_TYPE_RGBA,
  769. BITS_PER_PIXEL,
  770. 0, 0, 0, 0, 0, 0, // RGB bits and shift, unused
  771. 8, // alpha bits
  772. 0, // alpha shift
  773. 0, // accum bits
  774. 0, 0, 0, 0, // accum RGBA
  775. 24, // depth bits
  776. 8, // stencil bits, avi added for stencil test
  777. 0,
  778. PFD_MAIN_PLANE,
  779. 0,
  780. 0, 0, 0
  781. };
  782. if (!(mhDC = GetDC(mWindowHandle)))
  783. {
  784. OSMessageBox("Cannot make GL device context", "Error", OSMB_OK);
  785. close();
  786. return false;
  787. }
  788. try
  789. {
  790. // ChoosePixelFormat may crash in case of faulty driver
  791. if (!(pixel_format = SafeChoosePixelFormat(mhDC, &pfd)))
  792. {
  793. OSMessageBox("Cannot find suitable pixel format", "Error",
  794. OSMB_OK);
  795. close();
  796. return false;
  797. }
  798. }
  799. catch (...)
  800. {
  801. llwarns << "ChoosePixelFormat() failed, with error code: "
  802. << GetLastError() << llendl;
  803. OSMessageBox("Error while selecting pixel format", "Error",
  804. OSMB_OK);
  805. close();
  806. return false;
  807. }
  808. // Verify what pixel format we actually received.
  809. if (!DescribePixelFormat(mhDC, pixel_format, sizeof(PIXELFORMATDESCRIPTOR),
  810. &pfd))
  811. {
  812. OSMessageBox("Cannot get pixel format description", "Error", OSMB_OK);
  813. close();
  814. return false;
  815. }
  816. if (pfd.cColorBits < 32)
  817. {
  818. OSMessageBox(
  819. "The viewer requires True Color (32 bits) to run in a window.\n"
  820. "Please go to Control Panels -> Display -> Settings and\n"
  821. "set the screen to 32 bits color.\n"
  822. "Alternately, if you choose to run fullscreen, The viewer\n"
  823. "will automatically adjust the screen each time it runs.",
  824. "Error",
  825. OSMB_OK);
  826. close();
  827. return false;
  828. }
  829. if (pfd.cAlphaBits < 8)
  830. {
  831. OSMessageBox(
  832. "The viewer is unable to run because it cannot get an 8 bit alpha\n"
  833. "channel. Usually this is due to video card driver issues.\n"
  834. "Please make sure you have the latest video card drivers installed.\n"
  835. "Also be sure your monitor is set to True Color (32 bits) in\n"
  836. "Control Panels -> Display -> Settings.\n"
  837. "If you continue to receive this message, contact customer service.",
  838. "Error",
  839. OSMB_OK);
  840. close();
  841. return false;
  842. }
  843. if (!SetPixelFormat(mhDC, pixel_format, &pfd))
  844. {
  845. OSMessageBox("Cannot set pixel format", "Error", OSMB_OK);
  846. close();
  847. return false;
  848. }
  849. if (!(mhRC = SafeCreateContext(mhDC)))
  850. {
  851. OSMessageBox("Cannot create GL rendering context", "Error", OSMB_OK);
  852. close();
  853. return false;
  854. }
  855. if (!wglMakeCurrent(mhDC, mhRC))
  856. {
  857. OSMessageBox("Cannot activate GL rendering context", "Error", OSMB_OK);
  858. close();
  859. return false;
  860. }
  861. gGLManager.initWGL(mhDC);
  862. HWND oldWND = NULL;
  863. HDC oldDC = NULL;
  864. HGLRC oldRC = NULL;
  865. if (epoxy_has_wgl_extension(mhDC, "WGL_ARB_pixel_format"))
  866. {
  867. // OK, at this point, use the ARB wglChoosePixelFormatsARB function to
  868. // see if we can get exactly what we want.
  869. GLint attrib_list[256];
  870. S32 cur_attrib = 0;
  871. attrib_list[cur_attrib++] = WGL_DEPTH_BITS_ARB;
  872. attrib_list[cur_attrib++] = 24;
  873. attrib_list[cur_attrib++] = WGL_STENCIL_BITS_ARB;
  874. attrib_list[cur_attrib++] = 8;
  875. attrib_list[cur_attrib++] = WGL_DRAW_TO_WINDOW_ARB;
  876. attrib_list[cur_attrib++] = GL_TRUE;
  877. attrib_list[cur_attrib++] = WGL_ACCELERATION_ARB;
  878. attrib_list[cur_attrib++] = WGL_FULL_ACCELERATION_ARB;
  879. attrib_list[cur_attrib++] = WGL_SUPPORT_OPENGL_ARB;
  880. attrib_list[cur_attrib++] = GL_TRUE;
  881. attrib_list[cur_attrib++] = WGL_DOUBLE_BUFFER_ARB;
  882. attrib_list[cur_attrib++] = GL_TRUE;
  883. attrib_list[cur_attrib++] = WGL_COLOR_BITS_ARB;
  884. attrib_list[cur_attrib++] = 24;
  885. attrib_list[cur_attrib++] = WGL_ALPHA_BITS_ARB;
  886. attrib_list[cur_attrib++] = 8;
  887. U32 end_attrib = 0;
  888. if (mFSAASamples > 0)
  889. {
  890. end_attrib = cur_attrib;
  891. attrib_list[cur_attrib++] = WGL_SAMPLE_BUFFERS_ARB;
  892. attrib_list[cur_attrib++] = GL_TRUE;
  893. attrib_list[cur_attrib++] = WGL_SAMPLES_ARB;
  894. attrib_list[cur_attrib++] = mFSAASamples;
  895. }
  896. // End the list
  897. attrib_list[cur_attrib++] = 0;
  898. GLint pixel_formats[256];
  899. U32 num_formats = 0;
  900. // First we try and get a 32 bit depth pixel format
  901. bool result = (bool)wglChoosePixelFormatARB(mhDC, attrib_list, NULL,
  902. 256, pixel_formats,
  903. &num_formats);
  904. while (!result && mFSAASamples > 0)
  905. {
  906. llwarns << "FSAASamples: " << mFSAASamples << " not supported."
  907. << llendl;
  908. // Try to decrease sample pixel number until to disable
  909. // anti-aliasing
  910. mFSAASamples /= 2;
  911. if (mFSAASamples < 2)
  912. {
  913. mFSAASamples = 0;
  914. }
  915. if (mFSAASamples > 0)
  916. {
  917. attrib_list[end_attrib + 3] = mFSAASamples;
  918. }
  919. else
  920. {
  921. cur_attrib = end_attrib;
  922. end_attrib = 0;
  923. attrib_list[cur_attrib++] = 0; // End
  924. }
  925. result = wglChoosePixelFormatARB(mhDC, attrib_list, NULL, 256,
  926. pixel_formats, &num_formats);
  927. if (result)
  928. {
  929. llwarns << "Only support FSAASamples: " << mFSAASamples << llendl;
  930. }
  931. }
  932. if (!result)
  933. {
  934. show_window_creation_error("Error after wglChoosePixelFormatARB 32 bits");
  935. close();
  936. return false;
  937. }
  938. if (!num_formats)
  939. {
  940. if (end_attrib > 0)
  941. {
  942. llinfos << "No valid pixel format for " << mFSAASamples
  943. << "x anti-aliasing." << llendl;
  944. attrib_list[end_attrib] = 0;
  945. if (!wglChoosePixelFormatARB(mhDC, attrib_list, NULL, 256,
  946. pixel_formats, &num_formats))
  947. {
  948. show_window_creation_error("Error after wglChoosePixelFormatARB 32 bits no AA");
  949. close();
  950. return false;
  951. }
  952. }
  953. if (!num_formats)
  954. {
  955. llinfos << "No 32 bit z-buffer, trying 24 bits instead"
  956. << llendl;
  957. // Try 24-bit format
  958. attrib_list[1] = 24;
  959. if (!wglChoosePixelFormatARB(mhDC, attrib_list, NULL, 256,
  960. pixel_formats, &num_formats))
  961. {
  962. show_window_creation_error("Error after wglChoosePixelFormatARB 24-bit");
  963. close();
  964. return false;
  965. }
  966. if (!num_formats)
  967. {
  968. llwarns << "Could not get 24 bit z-buffer,trying 16 bits instead !"
  969. << llendl;
  970. attrib_list[1] = 16;
  971. bool result = (bool)wglChoosePixelFormatARB(mhDC,
  972. attrib_list,
  973. NULL, 256,
  974. pixel_formats,
  975. &num_formats);
  976. if (!result || !num_formats)
  977. {
  978. show_window_creation_error("Error after wglChoosePixelFormatARB 16-bit");
  979. close();
  980. return false;
  981. }
  982. }
  983. }
  984. llinfos << "Choosing pixel formats: " << num_formats
  985. << " pixel formats returned" << llendl;
  986. }
  987. // SL-14705: fix name tags showing in front of objects with AMD GPUs.
  988. // On AMD hardware we need to iterate from the first pixel format to
  989. // the end. Reference:
  990. // https://www.khronos.org/registry/OpenGL/extensions/ARB/WGL_ARB_pixel_format.txt
  991. const S32 max_format = (S32)num_formats - 1;
  992. S32 cur_format = 0;
  993. S32 swap_method = 0;
  994. GLint swap_query = WGL_SWAP_METHOD_ARB;
  995. while (wglGetPixelFormatAttribivARB(mhDC, pixel_formats[cur_format], 0,
  996. 1, &swap_query, &swap_method))
  997. {
  998. if (swap_method == WGL_SWAP_UNDEFINED_ARB)
  999. {
  1000. break;
  1001. }
  1002. if (cur_format >= max_format)
  1003. {
  1004. cur_format = 0;
  1005. break;
  1006. }
  1007. ++cur_format;
  1008. }
  1009. pixel_format = pixel_formats[cur_format];
  1010. if (mWindowHandle)
  1011. {
  1012. if (mhDC) // Does the window have a device context ?
  1013. {
  1014. if (mhRC)
  1015. {
  1016. oldRC = mhRC;
  1017. mhRC = NULL; // Zero the rendering context
  1018. }
  1019. oldDC = mhDC;
  1020. mhDC = NULL; // Zero the device context
  1021. }
  1022. oldWND = mWindowHandle;
  1023. }
  1024. mWindowHandle = CreateWindowEx(dw_ex_style, mWindowClassName,
  1025. mWindowTitle,
  1026. WS_CLIPSIBLINGS | WS_CLIPCHILDREN |
  1027. dw_style,
  1028. window_rect.left, window_rect.top,
  1029. window_rect.right - window_rect.left,
  1030. window_rect.bottom - window_rect.top,
  1031. NULL, NULL, mhInstance, NULL);
  1032. if (mWindowHandle)
  1033. {
  1034. llinfos << "Window has been recreated." << llendl;
  1035. }
  1036. else
  1037. {
  1038. llwarns << "Failed to recreate window). Error code: "
  1039. << GetLastError() << llendl;
  1040. }
  1041. if (!(mhDC = GetDC(mWindowHandle)))
  1042. {
  1043. OSMessageBox("Cannot make GL device context", "Error", OSMB_OK);
  1044. close();
  1045. return false;
  1046. }
  1047. if (!SetPixelFormat(mhDC, pixel_format, &pfd))
  1048. {
  1049. OSMessageBox("Cannot set pixel format", "Error", OSMB_OK);
  1050. close();
  1051. return false;
  1052. }
  1053. if (wglGetPixelFormatAttribivARB(mhDC, pixel_format, 0, 1, &swap_query,
  1054. &swap_method))
  1055. {
  1056. switch (swap_method)
  1057. {
  1058. case WGL_SWAP_EXCHANGE_ARB:
  1059. mSwapMethod = SWAP_METHOD_EXCHANGE;
  1060. LL_DEBUGS("Window") << "Swap Method: Exchange" << LL_ENDL;
  1061. break;
  1062. case WGL_SWAP_COPY_ARB:
  1063. mSwapMethod = SWAP_METHOD_COPY;
  1064. LL_DEBUGS("Window") << "Swap Method: Copy" << LL_ENDL;
  1065. break;
  1066. case WGL_SWAP_UNDEFINED_ARB:
  1067. default:
  1068. mSwapMethod = SWAP_METHOD_UNDEFINED;
  1069. LL_DEBUGS("Window") << "Swap Method: Undefined" << LL_ENDL;
  1070. }
  1071. }
  1072. }
  1073. else
  1074. {
  1075. llwarns << "No wgl_ARB_pixel_format extension, using default ChoosePixelFormat "
  1076. << llendl;
  1077. }
  1078. // Verify what pixel format we actually received.
  1079. if (!DescribePixelFormat(mhDC, pixel_format, sizeof(PIXELFORMATDESCRIPTOR),
  1080. &pfd))
  1081. {
  1082. OSMessageBox("Cannot get pixel format description", "Error", OSMB_OK);
  1083. close();
  1084. return false;
  1085. }
  1086. llinfos << "GL buffer: Color Bits " << S32(pfd.cColorBits)
  1087. << " Alpha Bits " << S32(pfd.cAlphaBits) << " Depth Bits "
  1088. << S32(pfd.cDepthBits) << llendl;
  1089. // Make sure we have 32 bits per pixel
  1090. if (pfd.cColorBits < 32 || GetDeviceCaps(mhDC, BITSPIXEL) < 32)
  1091. {
  1092. OSMessageBox(
  1093. "The viewer requires True Color (32 bits) to run in a window.\n"
  1094. "Please go to Control Panels -> Display -> Settings and\n"
  1095. "set the screen to 32 bits color.\n"
  1096. "Alternately, if you choose to run fullscreen, The viewer\n"
  1097. "will automatically adjust the screen each time it runs.",
  1098. "Error",
  1099. OSMB_OK);
  1100. close();
  1101. return false;
  1102. }
  1103. if (pfd.cAlphaBits < 8)
  1104. {
  1105. OSMessageBox(
  1106. "The viewer is unable to run because it cannot get an 8 bit alpha\n"
  1107. "channel. Usually this is due to video card driver issues.\n"
  1108. "Please make sure you have the latest video card drivers installed.\n"
  1109. "Also be sure your monitor is set to True Color (32 bits) in\n"
  1110. "Control Panels -> Display -> Settings.\n"
  1111. "If you continue to receive this message, contact customer service.",
  1112. "Error",
  1113. OSMB_OK);
  1114. close();
  1115. return false;
  1116. }
  1117. mhRC = NULL;
  1118. if (epoxy_has_wgl_extension(mhDC, "WGL_ARB_create_context"))
  1119. {
  1120. // Attempt to create a specific versioned context
  1121. mhRC = (HGLRC)createSharedContext();
  1122. if (!mhRC)
  1123. {
  1124. OSMessageBox("Cannot create versioned context", "Error", OSMB_OK);
  1125. close();
  1126. return false;
  1127. }
  1128. }
  1129. if (!wglMakeCurrent(mhDC, mhRC))
  1130. {
  1131. OSMessageBox("Cannot activate GL rendering context", "Error", OSMB_OK);
  1132. close();
  1133. return false;
  1134. }
  1135. if (oldWND)
  1136. {
  1137. if (oldDC)
  1138. {
  1139. if (oldRC)
  1140. {
  1141. wglDeleteContext(oldRC);
  1142. oldRC = NULL;
  1143. }
  1144. ReleaseDC(oldWND, oldDC);
  1145. oldDC = NULL;
  1146. }
  1147. destroy_window_handler(oldWND);
  1148. oldWND = NULL;
  1149. }
  1150. if (!gGLManager.initGL())
  1151. {
  1152. OSMessageBox("The viewer is unable to run because your video card drivers\n"
  1153. "did not install properly, are out of date, or are for unsupported\n"
  1154. "hardware. Please make sure you have the latest video card drivers\n"
  1155. "and even if you do have the latest, try reinstalling them.\n\n"
  1156. "If you continue to receive this message, contact customer service.",
  1157. "Error",
  1158. OSMB_OK);
  1159. close();
  1160. return false;
  1161. }
  1162. // Disable vertical sync for swap
  1163. if (epoxy_has_wgl_extension(mhDC, "WGL_EXT_swap_control"))
  1164. {
  1165. LL_DEBUGS("Window") << (disable_vsync ? "En" : "Dis")
  1166. << "abling vertical sync" << LL_ENDL;
  1167. wglSwapIntervalEXT(disable_vsync ? 0 : 1);
  1168. }
  1169. SetWindowLongPtr(mWindowHandle, GWLP_USERDATA, (LONG_PTR)this);
  1170. // Register joystick timer callback
  1171. SetTimer(mWindowHandle, 0, 1000.f / 30.f, NULL); // 30 fps timer
  1172. // OK to post quit messages now
  1173. mPostQuit = true;
  1174. if (auto_show)
  1175. {
  1176. show();
  1177. glClearColor(0.f, 0.f, 0.f, 0.f);
  1178. glClear(GL_COLOR_BUFFER_BIT);
  1179. swapBuffers();
  1180. }
  1181. return true;
  1182. }
  1183. //virtual
  1184. void* LLWindowWin32::createSharedContext()
  1185. {
  1186. S32 attribs[] =
  1187. {
  1188. // Start at 4.6
  1189. WGL_CONTEXT_MAJOR_VERSION_ARB, 4,
  1190. WGL_CONTEXT_MINOR_VERSION_ARB, 6,
  1191. WGL_CONTEXT_PROFILE_MASK_ARB,
  1192. LLRender::sGLCoreProfile ? WGL_CONTEXT_CORE_PROFILE_BIT_ARB
  1193. : WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB,
  1194. WGL_CONTEXT_FLAGS_ARB, gDebugGL ? WGL_CONTEXT_DEBUG_BIT_ARB : 0,
  1195. 0
  1196. };
  1197. while (true)
  1198. {
  1199. HGLRC rc = wglCreateContextAttribsARB(mhDC, mhRC, attribs);
  1200. if (rc)
  1201. {
  1202. llinfos << "Created OpenGL "
  1203. << llformat("%d.%d", attribs[1], attribs[3])
  1204. << (LLRender::sGLCoreProfile ? " core" : " compatibility")
  1205. << " context." << llendl;
  1206. return (void*)rc;
  1207. }
  1208. if (attribs[3] > 0)
  1209. {
  1210. // Decrement minor version
  1211. --attribs[3];
  1212. }
  1213. else if (attribs[1] > 3)
  1214. {
  1215. // Decrement major version and start minor version over at 3
  1216. --attribs[1];
  1217. attribs[3] = 3;
  1218. }
  1219. else
  1220. {
  1221. // We reached 3.0 and still failed, bail out
  1222. break;
  1223. }
  1224. }
  1225. return (void*)wglCreateContext(mhDC);
  1226. }
  1227. //virtual
  1228. void LLWindowWin32::makeContextCurrent(void* context)
  1229. {
  1230. if (!mhDC)
  1231. {
  1232. llerrs << "Trying to make a context current on a destroyed device context."
  1233. << llendl;
  1234. }
  1235. if (context)
  1236. {
  1237. wglMakeCurrent(mhDC, (HGLRC)context);
  1238. }
  1239. else
  1240. {
  1241. // Restore main GL thread context.
  1242. wglMakeCurrent(mhDC, mhRC);
  1243. }
  1244. }
  1245. //virtual
  1246. void LLWindowWin32::destroySharedContext(void* context)
  1247. {
  1248. if (context) // Ignore attempts to destroy invalid contexts. HB
  1249. {
  1250. wglDeleteContext((HGLRC)context);
  1251. }
  1252. }
  1253. void LLWindowWin32::moveWindow(const LLCoordScreen& position,
  1254. const LLCoordScreen& size)
  1255. {
  1256. if (mIsMouseClipping)
  1257. {
  1258. RECT client_rect_in_screen_space;
  1259. if (getClientRectInScreenSpace(&client_rect_in_screen_space))
  1260. {
  1261. ClipCursor(&client_rect_in_screen_space);
  1262. }
  1263. }
  1264. // If the window was already maximized, MoveWindow seems to still set the
  1265. // maximized flag even if the window is smaller than maximized. So we're
  1266. // going to do a restore first (which is a ShowWindow call) (SL-44655).
  1267. #if 0 // THIS CAUSES DEV-15484 and DEV-15949
  1268. ShowWindow(mWindowHandle, SW_RESTORE);
  1269. #endif
  1270. // NOW we can call MoveWindow
  1271. MoveWindow(mWindowHandle, position.mX, position.mY, size.mX, size.mY,
  1272. TRUE);
  1273. }
  1274. bool LLWindowWin32::setCursorPosition(const LLCoordWindow& position)
  1275. {
  1276. mMousePositionModified = true;
  1277. if (!mWindowHandle)
  1278. {
  1279. return false;
  1280. }
  1281. LLCoordScreen screen_pos;
  1282. if (!convertCoords(position, &screen_pos))
  1283. {
  1284. return false;
  1285. }
  1286. // Inform the application of the new mouse position (needed for per-frame
  1287. // hover/picking to function).
  1288. LLCoordGL gl_pos;
  1289. convertCoords(position, &gl_pos);
  1290. mCallbacks->handleMouseMove(this, gl_pos, (MASK)0);
  1291. // DEV-18951 VWR-8524 Camera moves wildly when alt-clicking. Because we
  1292. // have preemptively notified the application of the new mouse position via
  1293. // handleMouseMove() above, we need to clear out any stale mouse move
  1294. // events. RN/JC
  1295. MSG msg;
  1296. while (PeekMessage(&msg, NULL, WM_MOUSEMOVE, WM_MOUSEMOVE, PM_REMOVE));
  1297. return (bool)SetCursorPos(screen_pos.mX, screen_pos.mY);
  1298. }
  1299. bool LLWindowWin32::getCursorPosition(LLCoordWindow* position)
  1300. {
  1301. POINT cursor_point;
  1302. if (!mWindowHandle || !GetCursorPos(&cursor_point))
  1303. {
  1304. return false;
  1305. }
  1306. LLCoordScreen screen_pos;
  1307. screen_pos.mX = cursor_point.x;
  1308. screen_pos.mY = cursor_point.y;
  1309. return convertCoords(screen_pos, position);
  1310. }
  1311. void LLWindowWin32::hideCursor()
  1312. {
  1313. while (ShowCursor(FALSE) >= 0)
  1314. {
  1315. // Nothing, wait for cursor to push down
  1316. }
  1317. mCursorHidden = true;
  1318. mHideCursorPermanent = true;
  1319. }
  1320. void LLWindowWin32::showCursor()
  1321. {
  1322. // Makes sure the cursor shows up
  1323. while (ShowCursor(TRUE) < 0)
  1324. {
  1325. // do nothing, wait for cursor to pop out
  1326. }
  1327. mCursorHidden = false;
  1328. mHideCursorPermanent = false;
  1329. }
  1330. void LLWindowWin32::showCursorFromMouseMove()
  1331. {
  1332. if (!mHideCursorPermanent)
  1333. {
  1334. showCursor();
  1335. }
  1336. }
  1337. void LLWindowWin32::hideCursorUntilMouseMove()
  1338. {
  1339. if (!mHideCursorPermanent && mMouseVanish)
  1340. {
  1341. hideCursor();
  1342. mHideCursorPermanent = false;
  1343. }
  1344. }
  1345. HCURSOR LLWindowWin32::loadColorCursor(LPCTSTR name)
  1346. {
  1347. return (HCURSOR)LoadImage(mhInstance, name, IMAGE_CURSOR,
  1348. 0, // Default width
  1349. 0, // Default height
  1350. LR_DEFAULTCOLOR);
  1351. }
  1352. void LLWindowWin32::initCursors()
  1353. {
  1354. mCursor[UI_CURSOR_ARROW] = LoadCursor(NULL, IDC_ARROW);
  1355. mCursor[UI_CURSOR_WAIT] = LoadCursor(NULL, IDC_WAIT);
  1356. mCursor[UI_CURSOR_HAND] = LoadCursor(NULL, IDC_HAND);
  1357. mCursor[UI_CURSOR_IBEAM] = LoadCursor(NULL, IDC_IBEAM);
  1358. mCursor[UI_CURSOR_CROSS] = LoadCursor(NULL, IDC_CROSS);
  1359. mCursor[UI_CURSOR_SIZENWSE] = LoadCursor(NULL, IDC_SIZENWSE);
  1360. mCursor[UI_CURSOR_SIZENESW] = LoadCursor(NULL, IDC_SIZENESW);
  1361. mCursor[UI_CURSOR_SIZEWE] = LoadCursor(NULL, IDC_SIZEWE);
  1362. mCursor[UI_CURSOR_SIZENS] = LoadCursor(NULL, IDC_SIZENS);
  1363. mCursor[UI_CURSOR_NO] = LoadCursor(NULL, IDC_NO);
  1364. mCursor[UI_CURSOR_WORKING] = LoadCursor(NULL, IDC_APPSTARTING);
  1365. HMODULE module = GetModuleHandle(NULL);
  1366. mCursor[UI_CURSOR_TOOLGRAB] = LoadCursor(module, TEXT("TOOLGRAB"));
  1367. mCursor[UI_CURSOR_TOOLLAND] = LoadCursor(module, TEXT("TOOLLAND"));
  1368. mCursor[UI_CURSOR_TOOLFOCUS] = LoadCursor(module, TEXT("TOOLFOCUS"));
  1369. mCursor[UI_CURSOR_TOOLCREATE] = LoadCursor(module, TEXT("TOOLCREATE"));
  1370. mCursor[UI_CURSOR_ARROWDRAG] = LoadCursor(module, TEXT("ARROWDRAG"));
  1371. mCursor[UI_CURSOR_ARROWCOPY] = LoadCursor(module, TEXT("ARROWCOPY"));
  1372. mCursor[UI_CURSOR_ARROWDRAGMULTI] = LoadCursor(module, TEXT("ARROWDRAGMULTI"));
  1373. mCursor[UI_CURSOR_ARROWCOPYMULTI] = LoadCursor(module, TEXT("ARROWCOPYMULTI"));
  1374. mCursor[UI_CURSOR_NOLOCKED] = LoadCursor(module, TEXT("NOLOCKED"));
  1375. mCursor[UI_CURSOR_ARROWLOCKED] = LoadCursor(module, TEXT("ARROWLOCKED"));
  1376. mCursor[UI_CURSOR_GRABLOCKED] = LoadCursor(module, TEXT("GRABLOCKED"));
  1377. mCursor[UI_CURSOR_TOOLTRANSLATE] = LoadCursor(module, TEXT("TOOLTRANSLATE"));
  1378. mCursor[UI_CURSOR_TOOLROTATE] = LoadCursor(module, TEXT("TOOLROTATE"));
  1379. mCursor[UI_CURSOR_TOOLSCALE] = LoadCursor(module, TEXT("TOOLSCALE"));
  1380. mCursor[UI_CURSOR_TOOLCAMERA] = LoadCursor(module, TEXT("TOOLCAMERA"));
  1381. mCursor[UI_CURSOR_TOOLPAN] = LoadCursor(module, TEXT("TOOLPAN"));
  1382. mCursor[UI_CURSOR_TOOLZOOMIN] = LoadCursor(module, TEXT("TOOLZOOMIN"));
  1383. mCursor[UI_CURSOR_TOOLPICKOBJECT3] = LoadCursor(module, TEXT("TOOLPICKOBJECT3"));
  1384. mCursor[UI_CURSOR_PIPETTE] = LoadCursor(module, TEXT("TOOLPIPETTE"));
  1385. mCursor[UI_CURSOR_TOOLPATHFINDING] = LoadCursor(module, TEXT("TOOLPATHFINDING"));
  1386. mCursor[UI_CURSOR_TOOLPATHFINDING_PATH_START_ADD] = LoadCursor(module, TEXT("TOOLPATHFINDINGPATHSTARTADD"));
  1387. mCursor[UI_CURSOR_TOOLPATHFINDING_PATH_START] = LoadCursor(module, TEXT("TOOLPATHFINDINGPATHSTART"));
  1388. mCursor[UI_CURSOR_TOOLPATHFINDING_PATH_END] = LoadCursor(module, TEXT("TOOLPATHFINDINGPATHEND"));
  1389. mCursor[UI_CURSOR_TOOLPATHFINDING_PATH_END_ADD] = LoadCursor(module, TEXT("TOOLPATHFINDINGPATHENDADD"));
  1390. mCursor[UI_CURSOR_TOOLNO] = LoadCursor(module, TEXT("TOOLNO"));
  1391. // Color cursors
  1392. mCursor[UI_CURSOR_TOOLSIT] = loadColorCursor(TEXT("TOOLSIT"));
  1393. mCursor[UI_CURSOR_TOOLBUY] = loadColorCursor(TEXT("TOOLBUY"));
  1394. mCursor[UI_CURSOR_TOOLPAY] = loadColorCursor(TEXT("TOOLPAY"));
  1395. mCursor[UI_CURSOR_TOOLOPEN] = loadColorCursor(TEXT("TOOLOPEN"));
  1396. mCursor[UI_CURSOR_TOOLPLAY] = loadColorCursor(TEXT("TOOLPLAY"));
  1397. mCursor[UI_CURSOR_TOOLPAUSE] = loadColorCursor(TEXT("TOOLPAUSE"));
  1398. mCursor[UI_CURSOR_TOOLMEDIAOPEN] = loadColorCursor(TEXT("TOOLMEDIAOPEN"));
  1399. // Note: custom cursors that are not found make LoadCursor() return NULL.
  1400. for (S32 i = 0; i < UI_CURSOR_COUNT; ++i)
  1401. {
  1402. if (!mCursor[i])
  1403. {
  1404. mCursor[i] = LoadCursor(NULL, IDC_ARROW);
  1405. }
  1406. }
  1407. }
  1408. //virtual
  1409. void LLWindowWin32::setCursor(ECursorType cursor)
  1410. {
  1411. if (mCursorFrozen)
  1412. {
  1413. return;
  1414. }
  1415. if (cursor == UI_CURSOR_ARROW && mBusyCount > 0)
  1416. {
  1417. cursor = UI_CURSOR_WORKING;
  1418. }
  1419. if (mCurrentCursor != cursor)
  1420. {
  1421. mCurrentCursor = cursor;
  1422. SetCursor(mCursor[cursor]);
  1423. }
  1424. }
  1425. void LLWindowWin32::captureMouse()
  1426. {
  1427. SetCapture(mWindowHandle);
  1428. }
  1429. void LLWindowWin32::releaseMouse()
  1430. {
  1431. ReleaseCapture();
  1432. }
  1433. void LLWindowWin32::delayInputProcessing()
  1434. {
  1435. mInputProcessingPaused = true;
  1436. }
  1437. void LLWindowWin32::gatherInput()
  1438. {
  1439. MSG msg;
  1440. int msg_count = 0;
  1441. while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE) &&
  1442. msg_count++ < MAX_MESSAGE_PER_UPDATE)
  1443. {
  1444. TranslateMessage(&msg);
  1445. DispatchMessage(&msg);
  1446. if (mInputProcessingPaused)
  1447. {
  1448. break;
  1449. }
  1450. // For async host by name support. Really hacky.
  1451. if (gAsyncMsgCallback && LL_WM_HOST_RESOLVED == msg.message)
  1452. {
  1453. gAsyncMsgCallback(msg);
  1454. }
  1455. }
  1456. mInputProcessingPaused = false;
  1457. // Clear this once we have processed all mouse messages that might have
  1458. // occurred after we slammed the mouse position
  1459. mMousePositionModified = false;
  1460. }
  1461. LRESULT CALLBACK LLWindowWin32::mainWindowProc(HWND h_wnd, UINT u_msg,
  1462. WPARAM w_param, LPARAM l_param)
  1463. {
  1464. LLWindowWin32* window_imp =
  1465. (LLWindowWin32*)GetWindowLongPtr(h_wnd, GWLP_USERDATA);
  1466. if (window_imp)
  1467. {
  1468. LLWindowCallbacks* callbacksp = window_imp->mCallbacks;
  1469. // Juggle to make sure we can get negative positions for when mouse is
  1470. // outside window.
  1471. LLCoordWindow window_coord((S32)(S16)LOWORD(l_param),
  1472. (S32)(S16)HIWORD(l_param));
  1473. LLCoordGL gl_coord;
  1474. // Pass along extended flag in mask
  1475. MASK mask = (l_param >> 16 & KF_EXTENDED) ? MASK_EXTENDED : 0x0;
  1476. bool eat_keystroke = true;
  1477. switch (u_msg)
  1478. {
  1479. RECT update_rect;
  1480. S32 update_width, update_height;
  1481. case WM_TIMER:
  1482. {
  1483. callbacksp->handleTimerEvent(window_imp);
  1484. break;
  1485. }
  1486. case WM_DEVICECHANGE:
  1487. {
  1488. if (gDebugWindowProc)
  1489. {
  1490. llinfos << " WM_DEVICECHANGE: wParam=" << w_param
  1491. << "; lParam=" << l_param << llendl;
  1492. }
  1493. if (w_param == DBT_DEVNODES_CHANGED ||
  1494. w_param == DBT_DEVICEARRIVAL)
  1495. {
  1496. if (callbacksp->handleDeviceChange(window_imp))
  1497. {
  1498. return 0;
  1499. }
  1500. }
  1501. break;
  1502. }
  1503. case WM_ERASEBKGND:
  1504. {
  1505. break;
  1506. }
  1507. case WM_PAINT:
  1508. {
  1509. GetUpdateRect(window_imp->mWindowHandle, &update_rect, FALSE);
  1510. update_width = update_rect.right - update_rect.left + 1;
  1511. update_height = update_rect.bottom - update_rect.top + 1;
  1512. callbacksp->handlePaint(window_imp, update_rect.left,
  1513. update_rect.top, update_width,
  1514. update_height);
  1515. break;
  1516. }
  1517. case WM_PARENTNOTIFY:
  1518. {
  1519. break;
  1520. }
  1521. // This message is sent whenever the cursor is moved in a window. You
  1522. // need to set the appropriate cursor appearance.
  1523. case WM_SETCURSOR:
  1524. {
  1525. // Only take control of cursor over client region of window
  1526. // This allows Windows(tm) to handle resize cursors, etc.
  1527. if (LOWORD(l_param) == HTCLIENT)
  1528. {
  1529. SetCursor(window_imp->mCursor[window_imp->mCurrentCursor]);
  1530. return 0;
  1531. }
  1532. break;
  1533. }
  1534. case WM_ENTERMENULOOP:
  1535. {
  1536. callbacksp->handleWindowBlock(window_imp);
  1537. break;
  1538. }
  1539. case WM_EXITMENULOOP:
  1540. {
  1541. callbacksp->handleWindowUnblock(window_imp);
  1542. break;
  1543. }
  1544. case WM_ACTIVATEAPP:
  1545. {
  1546. // This message should be sent whenever the app gains or loses
  1547. // focus.
  1548. bool activating = (bool)w_param;
  1549. bool minimized = window_imp->getMinimized();
  1550. bool fullscreen = window_imp->mFullscreen;
  1551. if (gDebugWindowProc)
  1552. {
  1553. llinfos << "WINDOWPROC ActivateApp. Activating: "
  1554. << (activating ? "yes" : "no") << " - Minimized: "
  1555. << (minimized ? "yes" : "no") << " - Fullscreen: "
  1556. << (fullscreen ? "yes" : "no") << llendl;
  1557. }
  1558. if (fullscreen)
  1559. {
  1560. // When we run fullscreen, restoring or minimizing the app
  1561. // needs to switch the screen resolution
  1562. if (activating)
  1563. {
  1564. window_imp->setFullscreenResolution();
  1565. window_imp->restore();
  1566. }
  1567. else
  1568. {
  1569. window_imp->minimize();
  1570. window_imp->resetDisplayResolution();
  1571. }
  1572. }
  1573. callbacksp->handleActivateApp(window_imp, activating);
  1574. break;
  1575. }
  1576. case WM_ACTIVATE:
  1577. {
  1578. // Can be one of WA_ACTIVE, WA_CLICKACTIVE, or WA_INACTIVE
  1579. bool activating = LOWORD(w_param) != WA_INACTIVE;
  1580. bool minimized = (bool)HIWORD(w_param);
  1581. if (!activating && window_imp->mPreeditor)
  1582. {
  1583. window_imp->interruptLanguageTextInput();
  1584. }
  1585. // JC - I am not sure why, but if we do not report that we
  1586. // handled the WM_ACTIVATE message, the WM_ACTIVATEAPP messages
  1587. // do not work properly when we run fullscreen.
  1588. if (gDebugWindowProc)
  1589. {
  1590. llinfos << "WINDOWPROC Activate. Activating: "
  1591. << (activating ? "yes" : "no") << " - Minimized: "
  1592. << (minimized ? "yes" : "no") << llendl;
  1593. }
  1594. // Do not handle this.
  1595. break;
  1596. }
  1597. case WM_QUERYOPEN:
  1598. // *TODO: use this to return a nice icon
  1599. break;
  1600. case WM_SYSCOMMAND:
  1601. {
  1602. switch (w_param)
  1603. {
  1604. case SC_KEYMENU:
  1605. // Disallow the ALT key from triggering the default
  1606. // system menu.
  1607. return 0;
  1608. case SC_SCREENSAVE:
  1609. case SC_MONITORPOWER:
  1610. // Eat screen save messages and prevent them !
  1611. return 0;
  1612. }
  1613. break;
  1614. }
  1615. case WM_CLOSE:
  1616. {
  1617. // Will the app allow the window to close?
  1618. if (callbacksp->handleCloseRequest(window_imp))
  1619. {
  1620. // Get the app to initiate cleanup.
  1621. callbacksp->handleQuit(window_imp);
  1622. // The app is responsible for calling destroyWindow when done
  1623. // with GL
  1624. }
  1625. return 0;
  1626. }
  1627. case WM_DESTROY:
  1628. {
  1629. if (window_imp->shouldPostQuit())
  1630. {
  1631. // Posts WM_QUIT with an exit code of 0
  1632. PostQuitMessage(0);
  1633. }
  1634. return 0;
  1635. }
  1636. case WM_COMMAND:
  1637. {
  1638. if (!HIWORD(w_param)) // this message is from a menu
  1639. {
  1640. callbacksp->handleMenuSelect(window_imp, LOWORD(w_param));
  1641. }
  1642. break;
  1643. }
  1644. case WM_SYSKEYDOWN:
  1645. {
  1646. // Allow system keys, such as ALT-F4 to be processed by Windows
  1647. eat_keystroke = false;
  1648. }
  1649. case WM_KEYDOWN:
  1650. {
  1651. LL_FAST_TIMER(FTM_KEYHANDLER);
  1652. // Do not know until wm_char comes in next:
  1653. window_imp->mKeyCharCode = 0;
  1654. window_imp->mKeyScanCode = (l_param >> 16) & 0xff;
  1655. window_imp->mKeyVirtualKey = w_param;
  1656. window_imp->mRawMsg = u_msg;
  1657. window_imp->mRawWParam = w_param;
  1658. window_imp->mRawLParam = l_param;
  1659. if (gDebugWindowProc)
  1660. {
  1661. llinfos << "Debug WindowProc WM_KEYDOWN - key "
  1662. << S32(w_param) << llendl;
  1663. }
  1664. if (gKeyboardp &&
  1665. gKeyboardp->handleKeyDown(w_param, mask) && eat_keystroke)
  1666. {
  1667. return 0;
  1668. }
  1669. // Pass on to windows if we did not handle it
  1670. break;
  1671. }
  1672. case WM_SYSKEYUP:
  1673. eat_keystroke = false;
  1674. case WM_KEYUP:
  1675. {
  1676. LL_FAST_TIMER(FTM_KEYHANDLER);
  1677. window_imp->mKeyScanCode = (l_param >> 16) & 0xff;
  1678. window_imp->mKeyVirtualKey = w_param;
  1679. window_imp->mRawMsg = u_msg;
  1680. window_imp->mRawWParam = w_param;
  1681. window_imp->mRawLParam = l_param;
  1682. if (gDebugWindowProc)
  1683. {
  1684. llinfos << "Debug WindowProc WM_KEYUP - key: "
  1685. << S32(w_param) << llendl;
  1686. }
  1687. if (gKeyboardp &&
  1688. gKeyboardp->handleKeyUp(w_param, mask) && eat_keystroke)
  1689. {
  1690. return 0;
  1691. }
  1692. // Pass on to windows
  1693. break;
  1694. }
  1695. case WM_IME_SETCONTEXT:
  1696. {
  1697. if (gDebugWindowProc)
  1698. {
  1699. llinfos << "WM_IME_SETCONTEXT" << llendl;
  1700. }
  1701. if (window_imp->mPreeditor)
  1702. {
  1703. l_param &= ~ISC_SHOWUICOMPOSITIONWINDOW;
  1704. // Invoke DefWinProc with the modified LPARAM.
  1705. }
  1706. break;
  1707. }
  1708. case WM_IME_STARTCOMPOSITION:
  1709. {
  1710. LL_FAST_TIMER(FTM_KEYHANDLER);
  1711. if (gDebugWindowProc)
  1712. {
  1713. llinfos << "WM_IME_STARTCOMPOSITION" << llendl;
  1714. }
  1715. if (window_imp->mPreeditor)
  1716. {
  1717. window_imp->handleStartCompositionMessage();
  1718. return 0;
  1719. }
  1720. break;
  1721. }
  1722. case WM_IME_ENDCOMPOSITION:
  1723. {
  1724. LL_FAST_TIMER(FTM_KEYHANDLER);
  1725. if (gDebugWindowProc)
  1726. {
  1727. llinfos << "WM_IME_ENDCOMPOSITION" << llendl;
  1728. }
  1729. if (window_imp->mPreeditor)
  1730. {
  1731. return 0;
  1732. }
  1733. break;
  1734. }
  1735. case WM_IME_COMPOSITION:
  1736. {
  1737. LL_FAST_TIMER(FTM_KEYHANDLER);
  1738. if (gDebugWindowProc)
  1739. {
  1740. llinfos << "WM_IME_COMPOSITION" << llendl;
  1741. }
  1742. if (window_imp->mPreeditor)
  1743. {
  1744. window_imp->handleCompositionMessage(l_param);
  1745. return 0;
  1746. }
  1747. break;
  1748. }
  1749. case WM_IME_REQUEST:
  1750. {
  1751. LL_FAST_TIMER(FTM_KEYHANDLER);
  1752. if (gDebugWindowProc)
  1753. {
  1754. llinfos << "WM_IME_REQUEST" << llendl;
  1755. }
  1756. if (window_imp->mPreeditor)
  1757. {
  1758. LRESULT result = 0;
  1759. if (window_imp->handleImeRequests(w_param, l_param, &result))
  1760. {
  1761. return result;
  1762. }
  1763. }
  1764. break;
  1765. }
  1766. case WM_CHAR:
  1767. {
  1768. LL_FAST_TIMER(FTM_KEYHANDLER);
  1769. window_imp->mKeyCharCode = w_param;
  1770. window_imp->mRawMsg = u_msg;
  1771. window_imp->mRawWParam = w_param;
  1772. window_imp->mRawLParam = l_param;
  1773. // Should really use WM_UNICHAR eventually, but it requires a
  1774. // specific Windows version and I need to figure out how that
  1775. // works. - Doug
  1776. // ... Well, I do not think so. How it works is explained in
  1777. // Win32 API document, but WM_UNICHAR did not work as specified
  1778. // at least on Windows XP SP1 Japanese version. I have never
  1779. // used it since then and I'm not sure whether it has been
  1780. // fixed now, but I do not think it is worth trying. The good
  1781. // old WM_CHAR works just fine even for supplementary
  1782. // characters. We just need to take care of surrogate pairs
  1783. // sent as two WM_CHAR's by ourselves. It is not that tough.
  1784. // - Alissa Sabre @ SL
  1785. if (gDebugWindowProc)
  1786. {
  1787. llinfos << "Debug WindowProc WM_CHAR - key "
  1788. << S32(w_param) << llendl;
  1789. }
  1790. // Even if LLWindowCallbacks::handleUnicodeChar(llwchar, bool)
  1791. // returned false, we *did* process the event, so I believe we
  1792. // should not pass it to DefWindowProc...
  1793. MASK mask = gKeyboardp ? gKeyboardp->currentMask(false) : 0;
  1794. window_imp->handleUnicodeUTF16((U16)w_param, mask);
  1795. return 0;
  1796. }
  1797. case WM_LBUTTONDOWN:
  1798. {
  1799. LL_FAST_TIMER(FTM_MOUSEHANDLER);
  1800. if (window_imp->mPreeditor)
  1801. {
  1802. window_imp->interruptLanguageTextInput();
  1803. }
  1804. // Because we move the cursor position in the app, we need to
  1805. // query to find out where the cursor at the time the event is
  1806. // handled. If we do not do this, many clicks could get
  1807. // buffered up, and if the first click changes the cursor
  1808. // position, all subsequent clicks will occur at the wrong
  1809. // location. JC
  1810. LLCoordWindow cursor_coord_window;
  1811. if (window_imp->mMousePositionModified)
  1812. {
  1813. window_imp->getCursorPosition(&cursor_coord_window);
  1814. window_imp->convertCoords(cursor_coord_window, &gl_coord);
  1815. }
  1816. else
  1817. {
  1818. window_imp->convertCoords(window_coord, &gl_coord);
  1819. }
  1820. MASK mask = gKeyboardp ? gKeyboardp->currentMask(true) : 0;
  1821. // Generate move event to update mouse coordinates
  1822. callbacksp->handleMouseMove(window_imp, gl_coord, mask);
  1823. if (callbacksp->handleMouseDown(window_imp, gl_coord, mask))
  1824. {
  1825. return 0;
  1826. }
  1827. break;
  1828. }
  1829. case WM_LBUTTONDBLCLK:
  1830. {
  1831. LL_FAST_TIMER(FTM_MOUSEHANDLER);
  1832. // Because we move the cursor position in the app, we need to
  1833. // query to find out where the cursor at the time the event is
  1834. // handled. If we do not do this, many clicks could get
  1835. // buffered up, and if the first click changes the cursor
  1836. // position, all subsequent clicks will occur at the wrong
  1837. // location. JC
  1838. LLCoordWindow cursor_coord_window;
  1839. if (window_imp->mMousePositionModified)
  1840. {
  1841. window_imp->getCursorPosition(&cursor_coord_window);
  1842. window_imp->convertCoords(cursor_coord_window, &gl_coord);
  1843. }
  1844. else
  1845. {
  1846. window_imp->convertCoords(window_coord, &gl_coord);
  1847. }
  1848. MASK mask = gKeyboardp ? gKeyboardp->currentMask(true) : 0;
  1849. // generate move event to update mouse coordinates
  1850. callbacksp->handleMouseMove(window_imp, gl_coord, mask);
  1851. if (callbacksp->handleDoubleClick(window_imp, gl_coord, mask))
  1852. {
  1853. return 0;
  1854. }
  1855. break;
  1856. }
  1857. case WM_LBUTTONUP:
  1858. {
  1859. LL_FAST_TIMER(FTM_MOUSEHANDLER);
  1860. // Because we move the cursor position in the app, we need to
  1861. // query to find out where the cursor at the time the event is
  1862. // handled. If we do not do this, many clicks could get
  1863. // buffered up, and if the first click changes the cursor
  1864. // position, all subsequent clicks will occur at the wrong
  1865. // location. JC
  1866. LLCoordWindow cursor_coord_window;
  1867. if (window_imp->mMousePositionModified)
  1868. {
  1869. window_imp->getCursorPosition(&cursor_coord_window);
  1870. window_imp->convertCoords(cursor_coord_window, &gl_coord);
  1871. }
  1872. else
  1873. {
  1874. window_imp->convertCoords(window_coord, &gl_coord);
  1875. }
  1876. MASK mask = gKeyboardp ? gKeyboardp->currentMask(true) : 0;
  1877. // generate move event to update mouse coordinates
  1878. callbacksp->handleMouseMove(window_imp, gl_coord, mask);
  1879. if (callbacksp->handleMouseUp(window_imp, gl_coord, mask))
  1880. {
  1881. return 0;
  1882. }
  1883. break;
  1884. }
  1885. case WM_RBUTTONDBLCLK:
  1886. case WM_RBUTTONDOWN:
  1887. {
  1888. LL_FAST_TIMER(FTM_MOUSEHANDLER);
  1889. if (window_imp->mPreeditor)
  1890. {
  1891. window_imp->interruptLanguageTextInput();
  1892. }
  1893. // Because we move the cursor position in the app, we need to
  1894. // query to find out where the cursor at the time the event is
  1895. // handled. If we do not do this, many clicks could get
  1896. // buffered up, and if the first click changes the cursor
  1897. // position, all subsequent clicks will occur at the wrong
  1898. // location. JC
  1899. LLCoordWindow cursor_coord_window;
  1900. if (window_imp->mMousePositionModified)
  1901. {
  1902. window_imp->getCursorPosition(&cursor_coord_window);
  1903. window_imp->convertCoords(cursor_coord_window, &gl_coord);
  1904. }
  1905. else
  1906. {
  1907. window_imp->convertCoords(window_coord, &gl_coord);
  1908. }
  1909. MASK mask = gKeyboardp ? gKeyboardp->currentMask(true) : 0;
  1910. // generate move event to update mouse coordinates
  1911. callbacksp->handleMouseMove(window_imp, gl_coord, mask);
  1912. if (callbacksp->handleRightMouseDown(window_imp, gl_coord, mask))
  1913. {
  1914. return 0;
  1915. }
  1916. break;
  1917. }
  1918. case WM_RBUTTONUP:
  1919. {
  1920. LL_FAST_TIMER(FTM_MOUSEHANDLER);
  1921. // Because we move the cursor position in the app, we need to
  1922. // query to find out where the cursor at the time the event is
  1923. // handled. If we do not do this, many clicks could get
  1924. // buffered up, and if the first click changes the cursor
  1925. // position, all subsequent clicks will occur at the wrong
  1926. // location. JC
  1927. LLCoordWindow cursor_coord_window;
  1928. if (window_imp->mMousePositionModified)
  1929. {
  1930. window_imp->getCursorPosition(&cursor_coord_window);
  1931. window_imp->convertCoords(cursor_coord_window, &gl_coord);
  1932. }
  1933. else
  1934. {
  1935. window_imp->convertCoords(window_coord, &gl_coord);
  1936. }
  1937. MASK mask = gKeyboardp ? gKeyboardp->currentMask(true) : 0;
  1938. // generate move event to update mouse coordinates
  1939. callbacksp->handleMouseMove(window_imp, gl_coord, mask);
  1940. if (callbacksp->handleRightMouseUp(window_imp, gl_coord, mask))
  1941. {
  1942. return 0;
  1943. }
  1944. }
  1945. break;
  1946. case WM_MBUTTONDOWN:
  1947. {
  1948. LL_FAST_TIMER(FTM_MOUSEHANDLER);
  1949. if (window_imp->mPreeditor)
  1950. {
  1951. window_imp->interruptLanguageTextInput();
  1952. }
  1953. // Because we move the cursor position in the app, we need to
  1954. // query to find out where the cursor at the time the event is
  1955. // handled. If we do not do this, many clicks could get
  1956. // buffered up, and if the first click changes the cursor
  1957. // position, all subsequent clicks will occur at the wrong
  1958. // location. JC
  1959. LLCoordWindow cursor_coord_window;
  1960. if (window_imp->mMousePositionModified)
  1961. {
  1962. window_imp->getCursorPosition(&cursor_coord_window);
  1963. window_imp->convertCoords(cursor_coord_window, &gl_coord);
  1964. }
  1965. else
  1966. {
  1967. window_imp->convertCoords(window_coord, &gl_coord);
  1968. }
  1969. MASK mask = gKeyboardp ? gKeyboardp->currentMask(true) : 0;
  1970. // generate move event to update mouse coordinates
  1971. callbacksp->handleMouseMove(window_imp, gl_coord, mask);
  1972. if (callbacksp->handleMiddleMouseDown(window_imp, gl_coord, mask))
  1973. {
  1974. return 0;
  1975. }
  1976. break;
  1977. }
  1978. case WM_MBUTTONUP:
  1979. {
  1980. LL_FAST_TIMER(FTM_MOUSEHANDLER);
  1981. // Because we move the cursor position in the app, we need to
  1982. // query to find out where the cursor at the time the event is
  1983. // handled. If we do not do this, many clicks could get
  1984. // buffered up, and if the first click changes the cursor
  1985. // position, all subsequent clicks will occur at the wrong
  1986. // location. JC
  1987. LLCoordWindow cursor_coord_window;
  1988. if (window_imp->mMousePositionModified)
  1989. {
  1990. window_imp->getCursorPosition(&cursor_coord_window);
  1991. window_imp->convertCoords(cursor_coord_window, &gl_coord);
  1992. }
  1993. else
  1994. {
  1995. window_imp->convertCoords(window_coord, &gl_coord);
  1996. }
  1997. MASK mask = gKeyboardp ? gKeyboardp->currentMask(true) : 0;
  1998. // generate move event to update mouse coordinates
  1999. callbacksp->handleMouseMove(window_imp, gl_coord, mask);
  2000. if (callbacksp->handleMiddleMouseUp(window_imp, gl_coord, mask))
  2001. {
  2002. return 0;
  2003. }
  2004. break;
  2005. }
  2006. case WM_MOUSEWHEEL:
  2007. {
  2008. LL_FAST_TIMER(FTM_MOUSEHANDLER);
  2009. static short z_delta = 0;
  2010. z_delta += HIWORD(w_param);
  2011. // Current mouse wheels report changes in increments of zDelta
  2012. // (+120, -120). Future, higher resolution mouse wheels may
  2013. // report smaller deltas. So we sum the deltas and only act
  2014. // when we have exceeded WHEEL_DELTA
  2015. // If the user rapidly spins the wheel, we can get messages
  2016. // with large deltas, like 480 or so. Thus we need to scroll
  2017. // more quickly.
  2018. if (z_delta <= -WHEEL_DELTA || WHEEL_DELTA <= z_delta)
  2019. {
  2020. short clicks = -z_delta / WHEEL_DELTA;
  2021. callbacksp->handleScrollWheel(window_imp, clicks);
  2022. z_delta = 0;
  2023. }
  2024. return 0;
  2025. }
  2026. // Handle mouse movement within the window
  2027. case WM_MOUSEMOVE:
  2028. {
  2029. LL_FAST_TIMER(FTM_MOUSEHANDLER);
  2030. window_imp->convertCoords(window_coord, &gl_coord);
  2031. MASK mask = gKeyboardp ? gKeyboardp->currentMask(true) : 0;
  2032. callbacksp->handleMouseMove(window_imp, gl_coord, mask);
  2033. return 0;
  2034. }
  2035. case WM_SIZE:
  2036. {
  2037. S32 width = S32(LOWORD(l_param));
  2038. S32 height = S32(HIWORD(l_param));
  2039. if (gDebugWindowProc)
  2040. {
  2041. bool maximized = w_param == SIZE_MAXIMIZED;
  2042. bool minimized = w_param == SIZE_MINIMIZED;
  2043. bool restored = w_param == SIZE_RESTORED;
  2044. llinfos << "WINDOWPROC - Size: " << width << "x" << height
  2045. << " - Maximized: " << (maximized ? "yes" : "no")
  2046. << " - Minimized: " << (minimized ? "yes" : "no")
  2047. << " - Restored: " << (restored ? "yes" : "no")
  2048. << llendl;
  2049. }
  2050. // There is an odd behavior with WM_SIZE that I would call a bug.
  2051. // If the window is maximized, and you call MoveWindow() with a
  2052. // size smaller than a maximized window, it ends up sending WM_SIZE
  2053. // with w_param set to SIZE_MAXIMIZED, which is not true. So the
  2054. // logic below does not work.
  2055. // Fixed it by calling ShowWindow(SW_RESTORE) first (see
  2056. // moveWindow() in this file). SL-44655
  2057. // If we are now restored, but we were not before, this means that
  2058. // the window was un-minimized.
  2059. if (w_param == SIZE_RESTORED &&
  2060. window_imp->mLastSizeWParam != SIZE_RESTORED)
  2061. {
  2062. callbacksp->handleActivate(window_imp, true);
  2063. }
  2064. // Handle case of window being maximized from fully minimized state
  2065. if (w_param == SIZE_MAXIMIZED &&
  2066. window_imp->mLastSizeWParam != SIZE_MAXIMIZED)
  2067. {
  2068. callbacksp->handleActivate(window_imp, true);
  2069. }
  2070. // Also handle the minimization case
  2071. if (w_param == SIZE_MINIMIZED &&
  2072. window_imp->mLastSizeWParam != SIZE_MINIMIZED)
  2073. {
  2074. callbacksp->handleActivate(window_imp, false);
  2075. }
  2076. // Actually resize all of our views
  2077. if (w_param != SIZE_MINIMIZED)
  2078. {
  2079. // Ignore updates for minimizing and minimized "windows"
  2080. callbacksp->handleResize(window_imp, LOWORD(l_param),
  2081. HIWORD(l_param));
  2082. }
  2083. window_imp->mLastSizeWParam = w_param;
  2084. return 0;
  2085. }
  2086. case WM_DPICHANGED:
  2087. {
  2088. LL_DEBUGS("Window") << "Got a WM_DPICHANGED event." << LL_ENDL;
  2089. if (!gHiDPISupport)
  2090. {
  2091. LL_DEBUGS("Window") << "Ignoring based on gHiDPISupport."
  2092. << LL_ENDL;
  2093. break;
  2094. }
  2095. if (gIgnoreHiDPIEvents)
  2096. {
  2097. LL_DEBUGS("Window") << "Ignoring based on gIgnoreHiDPIEvents."
  2098. << LL_ENDL;
  2099. break;
  2100. }
  2101. LPRECT lprc_new_scale = (LPRECT)l_param;
  2102. F32 scale = F32(LOWORD(w_param)) / F32(USER_DEFAULT_SCREEN_DPI);
  2103. S32 width = lprc_new_scale->right - lprc_new_scale->left;
  2104. S32 height = lprc_new_scale->bottom - lprc_new_scale->top;
  2105. if (callbacksp->handleDPIChanged(window_imp, scale, width, height))
  2106. {
  2107. SetWindowPos(h_wnd, HWND_TOP, lprc_new_scale->left,
  2108. lprc_new_scale->top, width, height,
  2109. SWP_NOZORDER | SWP_NOACTIVATE);
  2110. }
  2111. return 0;
  2112. }
  2113. case WM_SETFOCUS:
  2114. {
  2115. if (gDebugWindowProc)
  2116. {
  2117. llinfos << "WINDOWPROC SetFocus" << llendl;
  2118. }
  2119. // Stop flashing the task bar button when our window gains focus
  2120. if (window_imp->mWindowHandle)
  2121. {
  2122. FLASHWINFO flash_info;
  2123. flash_info.cbSize = sizeof(FLASHWINFO);
  2124. flash_info.hwnd = window_imp->mWindowHandle;
  2125. flash_info.dwFlags = FLASHW_STOP;
  2126. flash_info.uCount = 0;
  2127. flash_info.dwTimeout = 0;
  2128. FlashWindowEx(&flash_info);
  2129. }
  2130. callbacksp->handleFocus(window_imp);
  2131. return 0;
  2132. }
  2133. case WM_KILLFOCUS:
  2134. {
  2135. if (gDebugWindowProc)
  2136. {
  2137. llinfos << "WINDOWPROC KillFocus" << llendl;
  2138. }
  2139. callbacksp->handleFocusLost(window_imp);
  2140. return 0;
  2141. }
  2142. case WM_COPYDATA:
  2143. {
  2144. // Received an URL
  2145. PCOPYDATASTRUCT myCDS = (PCOPYDATASTRUCT)l_param;
  2146. callbacksp->handleDataCopy(window_imp, myCDS->dwData,
  2147. myCDS->lpData);
  2148. return 0;
  2149. }
  2150. case WM_SETTINGCHANGE:
  2151. {
  2152. if (w_param == SPI_SETMOUSEVANISH &&
  2153. !SystemParametersInfo(SPI_GETMOUSEVANISH, 0,
  2154. &window_imp->mMouseVanish, 0))
  2155. {
  2156. window_imp->mMouseVanish = true;
  2157. }
  2158. break;
  2159. }
  2160. default:
  2161. if (gDebugWindowProc)
  2162. {
  2163. llinfos << "Unhandled windows message code: " << U32(u_msg)
  2164. << llendl;
  2165. }
  2166. }
  2167. }
  2168. // Pass unhandled messages down to Windows
  2169. return DefWindowProc(h_wnd, u_msg, w_param, l_param);
  2170. }
  2171. bool LLWindowWin32::convertCoords(LLCoordGL from, LLCoordWindow* to)
  2172. {
  2173. S32 client_height;
  2174. RECT client_rect;
  2175. if (!mWindowHandle || !to ||
  2176. !GetClientRect(mWindowHandle, &client_rect))
  2177. {
  2178. return false;
  2179. }
  2180. to->mX = from.mX;
  2181. client_height = client_rect.bottom - client_rect.top;
  2182. to->mY = client_height - from.mY - 1;
  2183. return true;
  2184. }
  2185. bool LLWindowWin32::convertCoords(LLCoordWindow from, LLCoordGL* to)
  2186. {
  2187. S32 client_height;
  2188. RECT client_rect;
  2189. if (!mWindowHandle || !to || !GetClientRect(mWindowHandle, &client_rect))
  2190. {
  2191. return false;
  2192. }
  2193. to->mX = from.mX;
  2194. client_height = client_rect.bottom - client_rect.top;
  2195. to->mY = client_height - from.mY - 1;
  2196. return true;
  2197. }
  2198. bool LLWindowWin32::convertCoords(LLCoordScreen from, LLCoordWindow* to)
  2199. {
  2200. if (!mWindowHandle || !to)
  2201. {
  2202. return false;
  2203. }
  2204. POINT mouse_point;
  2205. mouse_point.x = from.mX;
  2206. mouse_point.y = from.mY;
  2207. bool result = (bool)ScreenToClient(mWindowHandle, &mouse_point);
  2208. if (result)
  2209. {
  2210. to->mX = mouse_point.x;
  2211. to->mY = mouse_point.y;
  2212. }
  2213. return result;
  2214. }
  2215. bool LLWindowWin32::convertCoords(LLCoordWindow from, LLCoordScreen* to)
  2216. {
  2217. if (!mWindowHandle || !to)
  2218. {
  2219. return false;
  2220. }
  2221. POINT mouse_point;
  2222. mouse_point.x = from.mX;
  2223. mouse_point.y = from.mY;
  2224. bool result = (bool)ClientToScreen(mWindowHandle, &mouse_point);
  2225. if (result)
  2226. {
  2227. to->mX = mouse_point.x;
  2228. to->mY = mouse_point.y;
  2229. }
  2230. return result;
  2231. }
  2232. bool LLWindowWin32::convertCoords(LLCoordScreen from, LLCoordGL* to)
  2233. {
  2234. LLCoordWindow window_coord;
  2235. return convertCoords(from, &window_coord) &&
  2236. convertCoords(window_coord, to);
  2237. }
  2238. bool LLWindowWin32::convertCoords(LLCoordGL from, LLCoordScreen* to)
  2239. {
  2240. LLCoordWindow window_coord;
  2241. return convertCoords(from, &window_coord) &&
  2242. convertCoords(window_coord, to);
  2243. }
  2244. bool LLWindowWin32::isClipboardTextAvailable()
  2245. {
  2246. return IsClipboardFormatAvailable(CF_UNICODETEXT);
  2247. }
  2248. // Funky UTF-16 to wide character conversion routines, needed for some
  2249. // characters (such as emojis) when pasting from the clipboard (for these,
  2250. // ll_convert_wide_to_wstring() does not work properly)...
  2251. static S32 wchart_to_llwchar(const wchar_t* inchars, llwchar* outchar)
  2252. {
  2253. const wchar_t* base = inchars;
  2254. wchar_t cur_char = *inchars++;
  2255. llwchar char32;
  2256. if (cur_char >= 0xd800 && cur_char <= 0xdfff)
  2257. {
  2258. char32 = ((llwchar)(cur_char - 0xd800)) << 10;
  2259. cur_char = *inchars++;
  2260. char32 += (llwchar)(cur_char - 0xdc00) + 0x0010000UL;
  2261. }
  2262. else
  2263. {
  2264. char32 = (llwchar)cur_char;
  2265. }
  2266. *outchar = char32;
  2267. return inchars - base;
  2268. }
  2269. static LLWString convert_wide_to_wstring(const wchar_t* in)
  2270. {
  2271. LLWString wout;
  2272. if (!in) return wout;
  2273. const wchar_t* p;
  2274. for (p = in; *p; ++p)
  2275. ;
  2276. size_t len = p - in;
  2277. if (len <= 0) return wout;
  2278. size_t i = 0;
  2279. const wchar_t* chars16 = &in[0];
  2280. while (i < len)
  2281. {
  2282. llwchar cur_char;
  2283. i += wchart_to_llwchar(chars16 + i, &cur_char);
  2284. wout += cur_char;
  2285. }
  2286. return wout;
  2287. }
  2288. bool LLWindowWin32::pasteTextFromClipboard(LLWString &dst)
  2289. {
  2290. bool success = false;
  2291. if (IsClipboardFormatAvailable(CF_UNICODETEXT))
  2292. {
  2293. if (OpenClipboard(mWindowHandle))
  2294. {
  2295. HGLOBAL h_data = GetClipboardData(CF_UNICODETEXT);
  2296. if (h_data)
  2297. {
  2298. wchar_t* utf16str = (wchar_t*)GlobalLock(h_data);
  2299. if (utf16str)
  2300. {
  2301. dst = convert_wide_to_wstring(utf16str);
  2302. LLWStringUtil::removeCRLF(dst);
  2303. GlobalUnlock(h_data);
  2304. success = true;
  2305. }
  2306. }
  2307. CloseClipboard();
  2308. }
  2309. }
  2310. return success;
  2311. }
  2312. bool LLWindowWin32::copyTextToClipboard(const LLWString& wstr)
  2313. {
  2314. bool success = false;
  2315. if (OpenClipboard(mWindowHandle))
  2316. {
  2317. EmptyClipboard();
  2318. // Provide a copy of the data in Unicode format.
  2319. LLWString sanitized_string(wstr);
  2320. LLWStringUtil::addCRLF(sanitized_string);
  2321. llutf16string out_utf16 = wstring_to_utf16str(sanitized_string);
  2322. const size_t size_utf16 = (out_utf16.length() + 1) * sizeof(WCHAR);
  2323. // Memory is allocated and then ownership of it is transfered to the system.
  2324. HGLOBAL hglobal_copy_utf16 = GlobalAlloc(GMEM_MOVEABLE, size_utf16);
  2325. if (hglobal_copy_utf16)
  2326. {
  2327. WCHAR* copy_utf16 = (WCHAR*)GlobalLock(hglobal_copy_utf16);
  2328. if (copy_utf16)
  2329. {
  2330. memcpy(copy_utf16, out_utf16.c_str(), size_utf16);
  2331. GlobalUnlock(hglobal_copy_utf16);
  2332. if (SetClipboardData(CF_UNICODETEXT, hglobal_copy_utf16))
  2333. {
  2334. success = true;
  2335. }
  2336. }
  2337. }
  2338. CloseClipboard();
  2339. }
  2340. return success;
  2341. }
  2342. //virtual
  2343. bool LLWindowWin32::isPrimaryTextAvailable()
  2344. {
  2345. return !mPrimaryClipboard.empty();
  2346. }
  2347. //virtual
  2348. bool LLWindowWin32::pasteTextFromPrimary(LLWString& text)
  2349. {
  2350. if (mPrimaryClipboard.empty())
  2351. {
  2352. return false;
  2353. }
  2354. text = mPrimaryClipboard;
  2355. return true;
  2356. }
  2357. // virtual
  2358. bool LLWindowWin32::copyTextToPrimary(const LLWString& text)
  2359. {
  2360. mPrimaryClipboard = text;
  2361. return true;
  2362. }
  2363. // Constrains the mouse to the window.
  2364. void LLWindowWin32::setMouseClipping(bool b)
  2365. {
  2366. if (b != mIsMouseClipping)
  2367. {
  2368. bool success = false;
  2369. if (b)
  2370. {
  2371. GetClipCursor(&mOldMouseClip);
  2372. RECT client_rect_in_screen_space;
  2373. if (getClientRectInScreenSpace(&client_rect_in_screen_space))
  2374. {
  2375. success = (bool)ClipCursor(&client_rect_in_screen_space);
  2376. }
  2377. }
  2378. else
  2379. {
  2380. // Must restore the old mouse clip, which may be set by another
  2381. // window.
  2382. success = (bool)ClipCursor(&mOldMouseClip);
  2383. SetRect(&mOldMouseClip, 0, 0, 0, 0);
  2384. }
  2385. if (success)
  2386. {
  2387. mIsMouseClipping = b;
  2388. }
  2389. }
  2390. }
  2391. bool LLWindowWin32::getClientRectInScreenSpace(RECT* rectp)
  2392. {
  2393. bool success = false;
  2394. RECT client_rect;
  2395. if (mWindowHandle && GetClientRect(mWindowHandle, &client_rect))
  2396. {
  2397. POINT top_left;
  2398. top_left.x = client_rect.left;
  2399. top_left.y = client_rect.top;
  2400. ClientToScreen(mWindowHandle, &top_left);
  2401. POINT bottom_right;
  2402. bottom_right.x = client_rect.right;
  2403. bottom_right.y = client_rect.bottom;
  2404. ClientToScreen(mWindowHandle, &bottom_right);
  2405. SetRect(rectp, top_left.x, top_left.y, bottom_right.x, bottom_right.y);
  2406. success = true;
  2407. }
  2408. return success;
  2409. }
  2410. void LLWindowWin32::flashIcon(F32 seconds)
  2411. {
  2412. FLASHWINFO flash_info;
  2413. flash_info.cbSize = sizeof(FLASHWINFO);
  2414. flash_info.hwnd = mWindowHandle;
  2415. flash_info.dwFlags = FLASHW_TRAY;
  2416. flash_info.uCount = UINT(seconds / ICON_FLASH_TIME);
  2417. flash_info.dwTimeout = DWORD(1000.f * ICON_FLASH_TIME);
  2418. FlashWindowEx(&flash_info);
  2419. }
  2420. bool LLWindowWin32::restoreGamma()
  2421. {
  2422. if (mCustomGammaSet)
  2423. {
  2424. mCustomGammaSet = false;
  2425. return (bool)SetDeviceGammaRamp(mhDC, mPrevGammaRamp);
  2426. }
  2427. return true;
  2428. }
  2429. bool LLWindowWin32::setGamma(F32 gamma)
  2430. {
  2431. mCurrentGamma = llclamp(gamma, 0.01f, 10.f);
  2432. LL_DEBUGS("Window") << "Setting gamma to " << mCurrentGamma << LL_ENDL;
  2433. // Get the previous gamma ramp to restore later.
  2434. if (!mCustomGammaSet)
  2435. {
  2436. if (!gGLManager.mIsIntel)
  2437. {
  2438. LL_DEBUGS("Window") << "Getting previous gamma ramp to restore it later"
  2439. << LL_ENDL;
  2440. if (!GetDeviceGammaRamp(mhDC, mPrevGammaRamp))
  2441. {
  2442. llwarns << "Failed to get the previous gamma ramp. Aborted."
  2443. << llendl;
  2444. return false;
  2445. }
  2446. }
  2447. mCustomGammaSet = true;
  2448. }
  2449. constexpr F32 ONE256TH = 1.f / 256.f;
  2450. F32 inv_gamma = 1.f / mCurrentGamma;
  2451. for (S32 i = 0; i < 256; ++i)
  2452. {
  2453. S32 value = (S32)(powf((F32)i * ONE256TH, inv_gamma) * 65535.f + 0.5f);
  2454. if (value > 65535)
  2455. {
  2456. value = 65535;
  2457. }
  2458. mCurrentGammaRamp[0][i] = mCurrentGammaRamp[1][i] =
  2459. mCurrentGammaRamp[2][i] = (WORD)value;
  2460. }
  2461. return SetDeviceGammaRamp(mhDC, mCurrentGammaRamp);
  2462. }
  2463. LLWindow::LLWindowResolution* LLWindowWin32::getSupportedResolutions(S32& num_resolutions)
  2464. {
  2465. if (!mSupportedResolutions)
  2466. {
  2467. mSupportedResolutions = new LLWindowResolution[MAX_NUM_RESOLUTIONS];
  2468. DEVMODE dev_mode;
  2469. mNumSupportedResolutions = 0;
  2470. for (S32 mode_num = 0; mNumSupportedResolutions < MAX_NUM_RESOLUTIONS;
  2471. ++mode_num)
  2472. {
  2473. if (!EnumDisplaySettings(NULL, mode_num, &dev_mode))
  2474. {
  2475. break;
  2476. }
  2477. S32 w = dev_mode.dmPelsWidth;
  2478. S32 h = dev_mode.dmPelsHeight;
  2479. if (dev_mode.dmBitsPerPel == BITS_PER_PIXEL && w >= 800 &&
  2480. h >= 600)
  2481. {
  2482. bool resolution_exists = false;
  2483. for (S32 i = 0; i < mNumSupportedResolutions; ++i)
  2484. {
  2485. if (mSupportedResolutions[i].mWidth == w &&
  2486. mSupportedResolutions[i].mHeight == h)
  2487. {
  2488. resolution_exists = true;
  2489. break;
  2490. }
  2491. }
  2492. if (!resolution_exists)
  2493. {
  2494. mSupportedResolutions[mNumSupportedResolutions].mWidth = w;
  2495. mSupportedResolutions[mNumSupportedResolutions++].mHeight = h;
  2496. }
  2497. }
  2498. }
  2499. }
  2500. num_resolutions = mNumSupportedResolutions;
  2501. return mSupportedResolutions;
  2502. }
  2503. F32 LLWindowWin32::getNativeAspectRatio()
  2504. {
  2505. if (mOverrideAspectRatio > 0.f)
  2506. {
  2507. return mOverrideAspectRatio;
  2508. }
  2509. if (mNativeAspectRatio > 0.f)
  2510. {
  2511. // We grabbed this value at startup, based on the user's desktop settings
  2512. return mNativeAspectRatio;
  2513. }
  2514. // RN: this hack presumes that the largest supported resolution is monitor-
  2515. // limited and that pixels in that mode are square, therefore defining the
  2516. // native aspect ratio of the monitor... This seems to work to a close
  2517. // approximation for most CRTs/LCDs.
  2518. S32 num_resolutions;
  2519. LLWindowResolution* resolutions = getSupportedResolutions(num_resolutions);
  2520. return (F32)resolutions[num_resolutions - 1].mWidth /
  2521. (F32)resolutions[num_resolutions - 1].mHeight;
  2522. }
  2523. F32 LLWindowWin32::getPixelAspectRatio()
  2524. {
  2525. F32 pixel_aspect = 1.f;
  2526. if (getFullscreen())
  2527. {
  2528. LLCoordScreen screen_size;
  2529. getSize(&screen_size);
  2530. pixel_aspect = getNativeAspectRatio() * (F32)screen_size.mY /
  2531. (F32)screen_size.mX;
  2532. }
  2533. return pixel_aspect;
  2534. }
  2535. // Change display resolution. Returns true if successful.
  2536. // protected
  2537. bool LLWindowWin32::setDisplayResolution(S32 width, S32 height, S32 bits,
  2538. S32 refresh)
  2539. {
  2540. DEVMODE dev_mode;
  2541. ::ZeroMemory(&dev_mode, sizeof(DEVMODE));
  2542. dev_mode.dmSize = sizeof(dev_mode);
  2543. // Do not change anything if we do not have to
  2544. if (EnumDisplaySettings(NULL, ENUM_CURRENT_SETTINGS, &dev_mode))
  2545. {
  2546. if (dev_mode.dmPelsWidth == width && dev_mode.dmPelsHeight == height &&
  2547. dev_mode.dmBitsPerPel == bits &&
  2548. dev_mode.dmDisplayFrequency == refresh)
  2549. {
  2550. // Display mode identical, do nothing
  2551. return true;
  2552. }
  2553. }
  2554. memset(&dev_mode, 0, sizeof(dev_mode));
  2555. dev_mode.dmSize = sizeof(dev_mode);
  2556. dev_mode.dmPelsWidth = width;
  2557. dev_mode.dmPelsHeight = height;
  2558. dev_mode.dmBitsPerPel = bits;
  2559. dev_mode.dmDisplayFrequency = refresh;
  2560. dev_mode.dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT |
  2561. DM_DISPLAYFREQUENCY;
  2562. // CDS_FULLSCREEN indicates that this is a temporary change to the device
  2563. // mode.
  2564. LONG cds_result = ChangeDisplaySettings(&dev_mode, CDS_FULLSCREEN);
  2565. bool success = DISP_CHANGE_SUCCESSFUL == cds_result;
  2566. if (!success)
  2567. {
  2568. llwarns << "setDisplayResolution failed, " << width << "x" << height
  2569. << "x" << bits << " @ " << refresh << llendl;
  2570. }
  2571. return success;
  2572. }
  2573. // protected
  2574. bool LLWindowWin32::setFullscreenResolution()
  2575. {
  2576. if (mFullscreen)
  2577. {
  2578. return setDisplayResolution(mFullscreenWidth, mFullscreenHeight,
  2579. mFullscreenBits, mFullscreenRefresh);
  2580. }
  2581. return false;
  2582. }
  2583. // protected
  2584. bool LLWindowWin32::resetDisplayResolution()
  2585. {
  2586. LL_DEBUGS("Window") << "Resetting the display resolution" << LL_ENDL;
  2587. bool success = ChangeDisplaySettings(NULL, 0) == DISP_CHANGE_SUCCESSFUL;
  2588. if (!success)
  2589. {
  2590. llwarns << "Failure to reset display resolution" << llendl;
  2591. }
  2592. LL_DEBUGS("Window") << "Display resolution reset done" << LL_ENDL;
  2593. return success;
  2594. }
  2595. void LLWindowWin32::swapBuffers()
  2596. {
  2597. if (mhDC)
  2598. {
  2599. LL_FAST_TIMER(FTM_SWAP);
  2600. SwapBuffers(mhDC);
  2601. }
  2602. }
  2603. //
  2604. // LLSplashScreenImp
  2605. //
  2606. LLSplashScreenWin32::LLSplashScreenWin32()
  2607. : mWindow(NULL)
  2608. {
  2609. }
  2610. void LLSplashScreenWin32::showImpl()
  2611. {
  2612. // This appears to work ???
  2613. HINSTANCE hinst = GetModuleHandle(NULL);
  2614. mWindow = CreateDialog(hinst, TEXT("SPLASHSCREEN"), NULL, // No parent
  2615. (DLGPROC)LLSplashScreenWin32::windowProc);
  2616. ShowWindow(mWindow, SW_SHOW);
  2617. }
  2618. void LLSplashScreenWin32::updateImpl(const std::string& mesg)
  2619. {
  2620. if (mWindow)
  2621. {
  2622. WCHAR w_mesg[1024];
  2623. mbstowcs(w_mesg, mesg.c_str(), 1024);
  2624. SendDlgItemMessage(mWindow, 666, // HACK: text id
  2625. WM_SETTEXT, FALSE, (LPARAM)w_mesg);
  2626. }
  2627. }
  2628. void LLSplashScreenWin32::hideImpl()
  2629. {
  2630. if (mWindow)
  2631. {
  2632. destroy_window_handler(mWindow);
  2633. mWindow = NULL;
  2634. }
  2635. }
  2636. //static
  2637. LRESULT CALLBACK LLSplashScreenWin32::windowProc(HWND h_wnd, UINT u_msg,
  2638. WPARAM w_param,
  2639. LPARAM l_param)
  2640. {
  2641. // Just give it to windows
  2642. return DefWindowProc(h_wnd, u_msg, w_param, l_param);
  2643. }
  2644. //
  2645. // Helper Funcs
  2646. //
  2647. S32 OSMessageBoxWin32(const std::string& text, const std::string& caption,
  2648. U32 type)
  2649. {
  2650. UINT uType;
  2651. switch (type)
  2652. {
  2653. case OSMB_OK:
  2654. uType = MB_OK;
  2655. break;
  2656. case OSMB_OKCANCEL:
  2657. uType = MB_OKCANCEL;
  2658. break;
  2659. case OSMB_YESNO:
  2660. uType = MB_YESNO;
  2661. break;
  2662. default:
  2663. uType = MB_OK;
  2664. }
  2665. int retval_win = MessageBoxW(NULL,
  2666. ll_convert_string_to_wide(text).c_str(),
  2667. ll_convert_string_to_wide(caption).c_str(),
  2668. uType);
  2669. S32 retval;
  2670. switch (retval_win)
  2671. {
  2672. case IDYES:
  2673. retval = OSBTN_YES;
  2674. break;
  2675. case IDNO:
  2676. retval = OSBTN_NO;
  2677. break;
  2678. case IDOK:
  2679. retval = OSBTN_OK;
  2680. break;
  2681. case IDCANCEL:
  2682. retval = OSBTN_CANCEL;
  2683. break;
  2684. default:
  2685. retval = OSBTN_CANCEL;
  2686. }
  2687. return retval;
  2688. }
  2689. void LLWindowWin32::spawnWebBrowser(const std::string& escaped_url, bool async)
  2690. {
  2691. bool found = false;
  2692. for (S32 i = 0; i < gURLProtocolWhitelistCount; ++i)
  2693. {
  2694. if (escaped_url.find(gURLProtocolWhitelist[i]) == 0)
  2695. {
  2696. found = true;
  2697. break;
  2698. }
  2699. }
  2700. if (!found)
  2701. {
  2702. llwarns << "spawn_web_browser() called for url with protocol not on whitelist: "
  2703. << escaped_url << llendl;
  2704. return;
  2705. }
  2706. llinfos << "Opening URL " << escaped_url << llendl;
  2707. // Replaced ShellExecute code with ShellExecuteEx since ShellExecute does
  2708. // not work reliablly on Vista.
  2709. // This is madness.. no, this is..
  2710. std::wstring url_utf16 = ll_convert_string_to_wide(escaped_url);
  2711. // let the OS decide what to use to open the URL
  2712. SHELLEXECUTEINFO sei = { sizeof(sei) };
  2713. // NOTE: this assumes that SL will stick around long enough to complete the
  2714. // DDE message exchange
  2715. // necessary for ShellExecuteEx to complete
  2716. if (async)
  2717. {
  2718. sei.fMask = SEE_MASK_ASYNCOK;
  2719. }
  2720. sei.nShow = SW_SHOWNORMAL;
  2721. sei.lpVerb = L"open";
  2722. sei.lpFile = url_utf16.c_str();
  2723. ShellExecuteEx(&sei);
  2724. }
  2725. // Make the raw keyboard data available - used to poke through to CEF so
  2726. // that it has access to the virtual keycodes etc that it needs.
  2727. LLSD LLWindowWin32::getNativeKeyData()
  2728. {
  2729. LLSD result = LLSD::emptyMap();
  2730. result["scan_code"] = (S32)mKeyScanCode;
  2731. result["virtual_key"] = (S32)mKeyVirtualKey;
  2732. result["msg"] = ll_sd_from_U32(mRawMsg);
  2733. result["w_param"] = ll_sd_from_U32(mRawWParam);
  2734. result["l_param"] = ll_sd_from_U32(mRawLParam);
  2735. return result;
  2736. }
  2737. void* LLWindowWin32::getPlatformWindow()
  2738. {
  2739. return (void*)mWindowHandle;
  2740. }
  2741. void LLWindowWin32::bringToFront()
  2742. {
  2743. BringWindowToTop(mWindowHandle);
  2744. }
  2745. // set (OS) window focus back to the client
  2746. void LLWindowWin32::focusClient()
  2747. {
  2748. SetFocus(mWindowHandle);
  2749. }
  2750. void LLWindowWin32::allowLanguageTextInput(LLPreeditor* preeditor, bool b)
  2751. {
  2752. if (sLanguageTextInputAllowed == b)
  2753. {
  2754. return;
  2755. }
  2756. if (!b && preeditor != mPreeditor)
  2757. {
  2758. // This condition may occur with a call to
  2759. // setEnabled(bool) from LLTextEditor or LLLineEditor
  2760. // when the control is not focused.
  2761. // We need to silently ignore the case so that
  2762. // the language input status of the focused control
  2763. // is not disturbed.
  2764. return;
  2765. }
  2766. // Take care of old and new preeditors.
  2767. if (preeditor != mPreeditor || !b)
  2768. {
  2769. if (sLanguageTextInputAllowed)
  2770. {
  2771. interruptLanguageTextInput();
  2772. }
  2773. mPreeditor = (b ? preeditor : NULL);
  2774. }
  2775. sLanguageTextInputAllowed = b;
  2776. if (sLanguageTextInputAllowed)
  2777. {
  2778. // Allowing: Restore the previous IME status, so that the user has a
  2779. // feeling that the previous text input continues naturally. Be
  2780. // careful, however, the IME status is meaningful only during the user
  2781. // keeps using same Input Locale (aka Keyboard Layout).
  2782. if (sWinIMEOpened && sWinInputLocale == GetKeyboardLayout(0))
  2783. {
  2784. HIMC himc = ImmGetContext(mWindowHandle);
  2785. ImmSetOpenStatus(himc, TRUE);
  2786. ImmSetConversionStatus(himc, sWinIMEConversionMode,
  2787. sWinIMESentenceMode);
  2788. ImmReleaseContext(mWindowHandle, himc);
  2789. }
  2790. }
  2791. else
  2792. {
  2793. // Disallowing: Turn off the IME so that succeeding key events bypass
  2794. // IME and come to us directly. However, do it after saving the current
  2795. // IME status. We need to restore the status when allowing language
  2796. // text input again.
  2797. sWinInputLocale = GetKeyboardLayout(0);
  2798. sWinIMEOpened = ImmIsIME(sWinInputLocale);
  2799. if (sWinIMEOpened)
  2800. {
  2801. HIMC himc = ImmGetContext(mWindowHandle);
  2802. sWinIMEOpened = ImmGetOpenStatus(himc);
  2803. if (sWinIMEOpened)
  2804. {
  2805. ImmGetConversionStatus(himc, &sWinIMEConversionMode,
  2806. &sWinIMESentenceMode);
  2807. // We need both ImmSetConversionStatus and ImmSetOpenStatus
  2808. // here to surely disable IME's keyboard hooking, because some
  2809. // IME reacts only on the former and some other on the latter.
  2810. ImmSetConversionStatus(himc, IME_CMODE_NOCONVERSION,
  2811. sWinIMESentenceMode);
  2812. ImmSetOpenStatus(himc, FALSE);
  2813. }
  2814. ImmReleaseContext(mWindowHandle, himc);
  2815. }
  2816. }
  2817. }
  2818. void LLWindowWin32::fillCandidateForm(const LLCoordGL& caret,
  2819. const LLRect& bounds,
  2820. CANDIDATEFORM* form)
  2821. {
  2822. LLCoordWindow caret_coord, top_left, bottom_right;
  2823. convertCoords(caret, &caret_coord);
  2824. convertCoords(LLCoordGL(bounds.mLeft, bounds.mTop), &top_left);
  2825. convertCoords(LLCoordGL(bounds.mRight, bounds.mBottom), &bottom_right);
  2826. memset(form, 0, sizeof(CANDIDATEFORM));
  2827. form->dwStyle = CFS_EXCLUDE;
  2828. form->ptCurrentPos.x = caret_coord.mX;
  2829. form->ptCurrentPos.y = caret_coord.mY;
  2830. form->rcArea.left = top_left.mX;
  2831. form->rcArea.top = top_left.mY;
  2832. form->rcArea.right = bottom_right.mX;
  2833. form->rcArea.bottom = bottom_right.mY;
  2834. }
  2835. // Put the IME window at the right place (near current text input). Point
  2836. // coordinates should be the top of the current text line.
  2837. void LLWindowWin32::setLanguageTextInput(const LLCoordGL& position)
  2838. {
  2839. if (sLanguageTextInputAllowed)
  2840. {
  2841. HIMC himc = ImmGetContext(mWindowHandle);
  2842. LLCoordWindow win_pos;
  2843. convertCoords(position, &win_pos);
  2844. if (win_pos.mX >= 0 && win_pos.mY >= 0 &&
  2845. (win_pos.mX != sWinIMEWindowPosition.mX ||
  2846. win_pos.mY != sWinIMEWindowPosition.mY))
  2847. {
  2848. COMPOSITIONFORM ime_form;
  2849. memset(&ime_form, 0, sizeof(ime_form));
  2850. ime_form.dwStyle = CFS_POINT;
  2851. ime_form.ptCurrentPos.x = win_pos.mX;
  2852. ime_form.ptCurrentPos.y = win_pos.mY;
  2853. ImmSetCompositionWindow(himc, &ime_form);
  2854. sWinIMEWindowPosition.set(win_pos.mX, win_pos.mY);
  2855. }
  2856. ImmReleaseContext(mWindowHandle, himc);
  2857. }
  2858. }
  2859. void LLWindowWin32::fillCharPosition(const LLCoordGL& caret,
  2860. const LLRect& bounds,
  2861. const LLRect& control,
  2862. IMECHARPOSITION* char_position)
  2863. {
  2864. LLCoordScreen caret_coord, top_left, bottom_right;
  2865. convertCoords(caret, &caret_coord);
  2866. convertCoords(LLCoordGL(bounds.mLeft, bounds.mTop), &top_left);
  2867. convertCoords(LLCoordGL(bounds.mRight, bounds.mBottom), &bottom_right);
  2868. char_position->pt.x = caret_coord.mX;
  2869. // Windows wants the coordinate of upper left corner of a character...
  2870. char_position->pt.y = top_left.mY;
  2871. char_position->cLineHeight = bottom_right.mY - top_left.mY;
  2872. char_position->rcDocument.left = top_left.mX;
  2873. char_position->rcDocument.top = top_left.mY;
  2874. char_position->rcDocument.right = bottom_right.mX;
  2875. char_position->rcDocument.bottom = bottom_right.mY;
  2876. }
  2877. void LLWindowWin32::fillCompositionLogfont(LOGFONT* logfont)
  2878. {
  2879. // Our font is a list of FreeType recognized font files that may not have a
  2880. // corresponding ones in Windows' fonts. Hence, we cannot simply tell
  2881. // Windows which font we are using. We will notify a _standard_ font for a
  2882. // current input locale instead. We use a hard-coded knowledge about the
  2883. // Windows' standard configuration to do so...
  2884. memset(logfont, 0, sizeof(LOGFONT));
  2885. const WORD lang_id = LOWORD(GetKeyboardLayout(0));
  2886. switch (PRIMARYLANGID(lang_id))
  2887. {
  2888. case LANG_CHINESE:
  2889. // We need to identify one of two Chinese fonts.
  2890. switch (SUBLANGID(lang_id))
  2891. {
  2892. case SUBLANG_CHINESE_SIMPLIFIED:
  2893. case SUBLANG_CHINESE_SINGAPORE:
  2894. logfont->lfCharSet = GB2312_CHARSET;
  2895. lstrcpy(logfont->lfFaceName, TEXT("SimHei"));
  2896. break;
  2897. case SUBLANG_CHINESE_TRADITIONAL:
  2898. case SUBLANG_CHINESE_HONGKONG:
  2899. case SUBLANG_CHINESE_MACAU:
  2900. default:
  2901. logfont->lfCharSet = CHINESEBIG5_CHARSET;
  2902. lstrcpy(logfont->lfFaceName, TEXT("MingLiU"));
  2903. break;
  2904. }
  2905. break;
  2906. case LANG_JAPANESE:
  2907. logfont->lfCharSet = SHIFTJIS_CHARSET;
  2908. lstrcpy(logfont->lfFaceName, TEXT("MS Gothic"));
  2909. break;
  2910. case LANG_KOREAN:
  2911. logfont->lfCharSet = HANGUL_CHARSET;
  2912. lstrcpy(logfont->lfFaceName, TEXT("Gulim"));
  2913. break;
  2914. default:
  2915. logfont->lfCharSet = ANSI_CHARSET;
  2916. lstrcpy(logfont->lfFaceName, TEXT("Tahoma"));
  2917. break;
  2918. }
  2919. logfont->lfHeight = mPreeditor->getPreeditFontSize();
  2920. logfont->lfWeight = FW_NORMAL;
  2921. }
  2922. U32 LLWindowWin32::fillReconvertString(const LLWString &text,
  2923. S32 focus, S32 focus_length,
  2924. RECONVERTSTRING* reconvert_string)
  2925. {
  2926. const llutf16string text_utf16 = wstring_to_utf16str(text);
  2927. const DWORD required_size = sizeof(RECONVERTSTRING) +
  2928. (text_utf16.length() + 1) * sizeof(WCHAR);
  2929. if (reconvert_string && reconvert_string->dwSize >= required_size)
  2930. {
  2931. const DWORD focus_utf16_at = wstring_utf16_length(text, 0, focus);
  2932. const DWORD focus_utf16_length = wstring_utf16_length(text, focus,
  2933. focus_length);
  2934. reconvert_string->dwVersion = 0;
  2935. reconvert_string->dwStrLen = text_utf16.length();
  2936. reconvert_string->dwStrOffset = sizeof(RECONVERTSTRING);
  2937. reconvert_string->dwCompStrLen = focus_utf16_length;
  2938. reconvert_string->dwCompStrOffset = focus_utf16_at * sizeof(WCHAR);
  2939. reconvert_string->dwTargetStrLen = 0;
  2940. reconvert_string->dwTargetStrOffset = focus_utf16_at * sizeof(WCHAR);
  2941. const LPWSTR text = (LPWSTR)((BYTE*)reconvert_string +
  2942. sizeof(RECONVERTSTRING));
  2943. memcpy(text, text_utf16.c_str(),
  2944. (text_utf16.length() + 1) * sizeof(WCHAR));
  2945. }
  2946. return required_size;
  2947. }
  2948. void LLWindowWin32::updateLanguageTextInputArea()
  2949. {
  2950. if (!mPreeditor)
  2951. {
  2952. return;
  2953. }
  2954. LLCoordGL caret_coord;
  2955. LLRect preedit_bounds;
  2956. if (mPreeditor->getPreeditLocation(-1, &caret_coord, &preedit_bounds, NULL))
  2957. {
  2958. mLanguageTextInputPointGL = caret_coord;
  2959. mLanguageTextInputAreaGL = preedit_bounds;
  2960. CANDIDATEFORM candidate_form;
  2961. fillCandidateForm(caret_coord, preedit_bounds, &candidate_form);
  2962. HIMC himc = ImmGetContext(mWindowHandle);
  2963. // Win32 document says there may be up to 4 candidate windows.
  2964. // This magic number 4 appears only in the document, and
  2965. // there are no constant/macro for the value...
  2966. for (int i = 3; i >= 0; --i)
  2967. {
  2968. candidate_form.dwIndex = i;
  2969. ImmSetCandidateWindow(himc, &candidate_form);
  2970. }
  2971. ImmReleaseContext(mWindowHandle, himc);
  2972. }
  2973. }
  2974. void LLWindowWin32::interruptLanguageTextInput()
  2975. {
  2976. if (mPreeditor)
  2977. {
  2978. HIMC himc = ImmGetContext(mWindowHandle);
  2979. ImmNotifyIME(himc, NI_COMPOSITIONSTR, CPS_COMPLETE, 0);
  2980. ImmReleaseContext(mWindowHandle, himc);
  2981. // Win32 document says there will be no composition string after
  2982. // NI_COMPOSITIONSTR returns. The following call to resetPreedit should
  2983. // be a NOP unless IME goes mad...
  2984. mPreeditor->resetPreedit();
  2985. }
  2986. }
  2987. void LLWindowWin32::handleStartCompositionMessage()
  2988. {
  2989. // Let IME know the font to use in feedback UI.
  2990. LOGFONT logfont;
  2991. fillCompositionLogfont(&logfont);
  2992. HIMC himc = ImmGetContext(mWindowHandle);
  2993. ImmSetCompositionFont(himc, &logfont);
  2994. ImmReleaseContext(mWindowHandle, himc);
  2995. }
  2996. // Handle WM_IME_COMPOSITION message.
  2997. void LLWindowWin32::handleCompositionMessage(U32 indexes)
  2998. {
  2999. if (!mPreeditor)
  3000. {
  3001. return;
  3002. }
  3003. bool needs_update = false;
  3004. LLWString result_string;
  3005. LLWString preedit_string;
  3006. S32 preedit_string_utf16_length = 0;
  3007. LLPreeditor::segment_lengths_t preedit_segment_lengths;
  3008. LLPreeditor::standouts_t preedit_standouts;
  3009. // Step I: Receive details of preedits from IME.
  3010. HIMC himc = ImmGetContext(mWindowHandle);
  3011. if (indexes & GCS_RESULTSTR)
  3012. {
  3013. LONG size = ImmGetCompositionString(himc, GCS_RESULTSTR, NULL, 0);
  3014. if (size >= 0)
  3015. {
  3016. const LPWSTR data = new WCHAR[size / sizeof(WCHAR) + 1];
  3017. size = ImmGetCompositionString(himc, GCS_RESULTSTR, data, size);
  3018. if (size > 0)
  3019. {
  3020. result_string =
  3021. ll_convert_wide_to_wstring(std::wstring(data,
  3022. size / sizeof(WCHAR)));
  3023. }
  3024. delete[] data;
  3025. needs_update = true;
  3026. }
  3027. }
  3028. if (indexes & GCS_COMPSTR)
  3029. {
  3030. LONG size = ImmGetCompositionString(himc, GCS_COMPSTR, NULL, 0);
  3031. if (size >= 0)
  3032. {
  3033. const LPWSTR data = new WCHAR[size / sizeof(WCHAR) + 1];
  3034. size = ImmGetCompositionString(himc, GCS_COMPSTR, data, size);
  3035. if (size)
  3036. {
  3037. preedit_string_utf16_length = size / sizeof(WCHAR);
  3038. preedit_string =
  3039. ll_convert_wide_to_wstring(std::wstring(data,
  3040. size / sizeof(WCHAR)));
  3041. }
  3042. delete[] data;
  3043. needs_update = true;
  3044. }
  3045. }
  3046. if ((indexes & GCS_COMPCLAUSE) && preedit_string.length())
  3047. {
  3048. LONG size = ImmGetCompositionString(himc, GCS_COMPCLAUSE, NULL, 0);
  3049. if (size > 0)
  3050. {
  3051. const LPDWORD data = new DWORD[size / sizeof(DWORD)];
  3052. size = ImmGetCompositionString(himc, GCS_COMPCLAUSE, data, size);
  3053. if (size >= sizeof(DWORD) * 2 && data[0] == 0 &&
  3054. data[size / sizeof(DWORD) - 1] == preedit_string_utf16_length)
  3055. {
  3056. preedit_segment_lengths.resize(size / sizeof(DWORD) - 1);
  3057. S32 offset = 0;
  3058. for (U32 i = 0; i < preedit_segment_lengths.size(); ++i)
  3059. {
  3060. const S32 length =
  3061. wstring_length_from_utf16_length(preedit_string,
  3062. offset,
  3063. data[i + 1] -
  3064. data[i]);
  3065. preedit_segment_lengths[i] = length;
  3066. offset += length;
  3067. }
  3068. }
  3069. delete[] data;
  3070. }
  3071. }
  3072. if ((indexes & GCS_COMPATTR) && preedit_segment_lengths.size() > 1)
  3073. {
  3074. LONG size = ImmGetCompositionString(himc, GCS_COMPATTR, NULL, 0);
  3075. if (size > 0)
  3076. {
  3077. const LPBYTE data = new BYTE[size / sizeof(BYTE)];
  3078. size = ImmGetCompositionString(himc, GCS_COMPATTR, data, size);
  3079. if (size == preedit_string_utf16_length)
  3080. {
  3081. preedit_standouts.assign(preedit_segment_lengths.size(),
  3082. false);
  3083. S32 offset = 0;
  3084. for (U32 i = 0; i < preedit_segment_lengths.size(); ++i)
  3085. {
  3086. if (ATTR_TARGET_CONVERTED == data[offset] ||
  3087. ATTR_TARGET_NOTCONVERTED == data[offset])
  3088. {
  3089. preedit_standouts[i] = true;
  3090. }
  3091. offset += wstring_utf16_length(preedit_string, offset,
  3092. preedit_segment_lengths[i]);
  3093. }
  3094. }
  3095. delete[] data;
  3096. }
  3097. }
  3098. S32 caret_position = preedit_string.length();
  3099. if (indexes & GCS_CURSORPOS)
  3100. {
  3101. const S32 caret_position_utf16 =
  3102. ImmGetCompositionString(himc, GCS_CURSORPOS, NULL, 0);
  3103. if (caret_position_utf16 >= 0 &&
  3104. caret_position <= preedit_string_utf16_length)
  3105. {
  3106. caret_position =
  3107. wstring_length_from_utf16_length(preedit_string, 0,
  3108. caret_position_utf16);
  3109. }
  3110. }
  3111. if (!indexes)
  3112. {
  3113. // I am not sure this condition really happens, but Windows SDK
  3114. // document says it is an indication of "reset everything."
  3115. needs_update = true;
  3116. }
  3117. ImmReleaseContext(mWindowHandle, himc);
  3118. // Step II: Update the active preeditor.
  3119. if (needs_update)
  3120. {
  3121. if (preedit_string.length() || result_string.length())
  3122. {
  3123. mPreeditor->resetPreedit();
  3124. }
  3125. if (result_string.length())
  3126. {
  3127. for (LLWString::const_iterator i = result_string.begin();
  3128. i != result_string.end(); ++i)
  3129. {
  3130. mPreeditor->handleUnicodeCharHere(*i);
  3131. }
  3132. }
  3133. if (!preedit_string.length())
  3134. {
  3135. preedit_segment_lengths.clear();
  3136. preedit_standouts.clear();
  3137. }
  3138. else
  3139. {
  3140. if (!preedit_segment_lengths.size())
  3141. {
  3142. preedit_segment_lengths.assign(1, preedit_string.length());
  3143. }
  3144. if (!preedit_standouts.size())
  3145. {
  3146. preedit_standouts.assign(preedit_segment_lengths.size(),
  3147. false);
  3148. }
  3149. }
  3150. mPreeditor->updatePreedit(preedit_string, preedit_segment_lengths,
  3151. preedit_standouts, caret_position);
  3152. // Some IME does not query char position after WM_IME_COMPOSITION, so
  3153. // we need to update them actively.
  3154. updateLanguageTextInputArea();
  3155. }
  3156. }
  3157. // Given a text and a focus range, finds and returns a surrounding context of
  3158. // the focused subtext. A variable pointed to by offset receives the offset in
  3159. // llwchars of the beginning of the returned context string in the given wtext.
  3160. static LLWString find_context(const LLWString& wtext, S32 focus,
  3161. S32 focus_length, S32 *offset)
  3162. {
  3163. constexpr S32 CONTEXT_EXCESS = 30; // This value is empirical
  3164. S32 e = llmin((S32)wtext.length(), focus + focus_length + CONTEXT_EXCESS);
  3165. S32 end = focus + focus_length;
  3166. while (end < e && '\n' != wtext[end])
  3167. {
  3168. end++;
  3169. }
  3170. S32 s = llmax(0, focus - CONTEXT_EXCESS);
  3171. S32 start = focus;
  3172. while (start > s && '\n' != wtext[start - 1])
  3173. {
  3174. --start;
  3175. }
  3176. *offset = start;
  3177. return wtext.substr(start, end - start);
  3178. }
  3179. // Handles WM_IME_REQUEST message. If it handled the message, returns true.
  3180. // Otherwise, false. When it handled the message, the value to be returned from
  3181. // the Window Procedure is set to *result.
  3182. bool LLWindowWin32::handleImeRequests(WPARAM request, LPARAM param,
  3183. LRESULT* result)
  3184. {
  3185. if (!mPreeditor)
  3186. {
  3187. return false;
  3188. }
  3189. switch (request)
  3190. {
  3191. // http://msdn2.microsoft.com/en-us/library/ms776080.aspx
  3192. case IMR_CANDIDATEWINDOW:
  3193. {
  3194. LLCoordGL caret_coord;
  3195. LLRect preedit_bounds;
  3196. mPreeditor->getPreeditLocation(-1, &caret_coord,
  3197. &preedit_bounds, NULL);
  3198. CANDIDATEFORM* const form = (CANDIDATEFORM*)param;
  3199. DWORD const dwIndex = form->dwIndex;
  3200. fillCandidateForm(caret_coord, preedit_bounds, form);
  3201. form->dwIndex = dwIndex;
  3202. *result = 1;
  3203. return true;
  3204. }
  3205. case IMR_QUERYCHARPOSITION:
  3206. {
  3207. IMECHARPOSITION* const char_position = (IMECHARPOSITION*)param;
  3208. // char_position->dwCharPos counts in number of
  3209. // WCHARs, i.e., UTF-16 encoding units, so we cannot simply
  3210. // pass the number to getPreeditLocation.
  3211. const LLWString& wtext = mPreeditor->getWText();
  3212. S32 preedit, preedit_length;
  3213. mPreeditor->getPreeditRange(&preedit, &preedit_length);
  3214. LLCoordGL caret_coord;
  3215. LLRect preedit_bounds, text_control;
  3216. S32 position =
  3217. wstring_length_from_utf16_length(wtext, preedit,
  3218. char_position->dwCharPos);
  3219. if (!mPreeditor->getPreeditLocation(position, &caret_coord,
  3220. &preedit_bounds,
  3221. &text_control))
  3222. {
  3223. llwarns << "IMR_QUERYCHARPOSITON called but getPreeditLocation() failed."
  3224. << llendl;
  3225. return false;
  3226. }
  3227. fillCharPosition(caret_coord, preedit_bounds, text_control,
  3228. char_position);
  3229. *result = 1;
  3230. return true;
  3231. }
  3232. case IMR_COMPOSITIONFONT:
  3233. {
  3234. fillCompositionLogfont((LOGFONT*)param);
  3235. *result = 1;
  3236. return true;
  3237. }
  3238. case IMR_RECONVERTSTRING:
  3239. {
  3240. mPreeditor->resetPreedit();
  3241. const LLWString & wtext = mPreeditor->getWText();
  3242. S32 select, select_length;
  3243. mPreeditor->getSelectionRange(&select, &select_length);
  3244. S32 context_offset;
  3245. const LLWString context = find_context(wtext, select,
  3246. select_length,
  3247. &context_offset);
  3248. RECONVERTSTRING* const reconvert_string = (RECONVERTSTRING*)param;
  3249. const U32 size = fillReconvertString(context,
  3250. select - context_offset,
  3251. select_length,
  3252. reconvert_string);
  3253. if (reconvert_string)
  3254. {
  3255. if (select_length == 0)
  3256. {
  3257. // Let the IME to decide the reconversion range, and adjust
  3258. // the reconvert_string structure accordingly.
  3259. HIMC himc = ImmGetContext(mWindowHandle);
  3260. BOOL adjusted =
  3261. ImmSetCompositionString(himc, SCS_QUERYRECONVERTSTRING,
  3262. reconvert_string, size, NULL,
  3263. 0);
  3264. ImmReleaseContext(mWindowHandle, himc);
  3265. if (adjusted)
  3266. {
  3267. const llutf16string& text_utf16 =
  3268. wstring_to_utf16str(context);
  3269. S32 new_preedit_start =
  3270. reconvert_string->dwCompStrOffset / sizeof(WCHAR);
  3271. S32 new_preedit_end =
  3272. new_preedit_start + reconvert_string->dwCompStrLen;
  3273. select = utf16str_wstring_length(text_utf16,
  3274. new_preedit_start);
  3275. select_length =
  3276. utf16str_wstring_length(text_utf16,
  3277. new_preedit_end) - select;
  3278. select += context_offset;
  3279. }
  3280. }
  3281. mPreeditor->markAsPreedit(select, select_length);
  3282. }
  3283. *result = size;
  3284. return true;
  3285. }
  3286. case IMR_CONFIRMRECONVERTSTRING:
  3287. {
  3288. *result = FALSE;
  3289. return true;
  3290. }
  3291. case IMR_DOCUMENTFEED:
  3292. {
  3293. const LLWString & wtext = mPreeditor->getWText();
  3294. S32 preedit, preedit_length;
  3295. mPreeditor->getPreeditRange(&preedit, &preedit_length);
  3296. S32 context_offset;
  3297. LLWString context = find_context(wtext, preedit, preedit_length,
  3298. &context_offset);
  3299. preedit -= context_offset;
  3300. if (preedit_length > 0 && preedit >= 0)
  3301. {
  3302. // IMR_DOCUMENTFEED may be called when we have an active
  3303. // preedit. We should pass the context string *excluding*
  3304. // the preedit string. Otherwise, some IME are confused.
  3305. context.erase(preedit, preedit_length);
  3306. }
  3307. RECONVERTSTRING* reconvert_string = (RECONVERTSTRING*)param;
  3308. *result = fillReconvertString(context, preedit, 0,
  3309. reconvert_string);
  3310. return true;
  3311. }
  3312. default:
  3313. break;
  3314. }
  3315. return false;
  3316. }
  3317. //static
  3318. void LLWindowWin32::setDPIAwareness()
  3319. {
  3320. HMODULE shcorep = LoadLibrary(L"shcore.dll");
  3321. if (!shcorep)
  3322. {
  3323. llwarns << "Could not load the shcore.dll library. Will use legacy DPI awareness API of Windows 7"
  3324. << llendl;
  3325. return;
  3326. }
  3327. SetProcessDpiAwarenessType spdap =
  3328. (SetProcessDpiAwarenessType)GetProcAddress(shcorep,
  3329. "SetProcessDpiAwareness");
  3330. if (spdap)
  3331. {
  3332. HRESULT hr = spdap(PROCESS_PER_MONITOR_DPI_AWARE);
  3333. if (hr != S_OK)
  3334. {
  3335. llwarns << "SetProcessDpiAwareness() returned an error; will use legacy DPI awareness API of Windows 7"
  3336. << llendl;
  3337. }
  3338. }
  3339. FreeLibrary(shcorep);
  3340. }
  3341. F32 LLWindowWin32::getSystemUISize()
  3342. {
  3343. F32 scale_value = 1.f;
  3344. if (!gHiDPISupport)
  3345. {
  3346. return scale_value;
  3347. }
  3348. HWND hwnd = (HWND)getPlatformWindow();
  3349. HDC hdc = GetDC(hwnd);
  3350. HMODULE shcorep = LoadLibrary(L"shcore.dll");
  3351. if (shcorep)
  3352. {
  3353. GetProcessDpiAwarenessType gpdap =
  3354. (GetProcessDpiAwarenessType)GetProcAddress(shcorep,
  3355. "GetProcessDpiAwareness");
  3356. GetDpiForMonitorType gdfmp =
  3357. (GetDpiForMonitorType)GetProcAddress(shcorep, "GetDpiForMonitor");
  3358. if (gpdap && gdfmp)
  3359. {
  3360. HANDLE hprocess = GetCurrentProcess();
  3361. PROCESS_DPI_AWARENESS dpi_awareness;
  3362. gpdap(hprocess, &dpi_awareness);
  3363. if (dpi_awareness == PROCESS_PER_MONITOR_DPI_AWARE)
  3364. {
  3365. RECT rect;
  3366. GetWindowRect(hwnd, &rect);
  3367. // Get the DPI for the monitor, on which the center of window
  3368. // is displayed and set the scaling factor
  3369. POINT pt;
  3370. pt.x = (rect.left + rect.right) / 2;
  3371. pt.y = (rect.top + rect.bottom) / 2;
  3372. HMONITOR hmonitor = MonitorFromPoint(pt,
  3373. MONITOR_DEFAULTTONEAREST);
  3374. UINT dpix = 0, dpiy = 0;
  3375. HRESULT hr = gdfmp(hmonitor, MDT_EFFECTIVE_DPI, &dpix, &dpiy);
  3376. if (hr == S_OK)
  3377. {
  3378. scale_value = F32(dpix) / F32(USER_DEFAULT_SCREEN_DPI);
  3379. }
  3380. else
  3381. {
  3382. llwarns << "Could not determine DPI for monitor; setting scale to 100%."
  3383. << llendl;
  3384. }
  3385. }
  3386. else
  3387. {
  3388. llwarns << "Process is not per-monitor DPI-aware; setting scale to 100%."
  3389. << llendl;
  3390. }
  3391. }
  3392. FreeLibrary(shcorep);
  3393. }
  3394. else
  3395. {
  3396. llwarns << "Could not load shcore.dll library; using legacy DPI awareness API of Windows 7."
  3397. << llendl;
  3398. scale_value = F32(GetDeviceCaps(hdc, LOGPIXELSX)) /
  3399. F32(USER_DEFAULT_SCREEN_DPI);
  3400. }
  3401. ReleaseDC(hwnd, hdc);
  3402. return scale_value;
  3403. }
  3404. //static
  3405. std::vector<std::string> LLWindowWin32::getDynamicFallbackFontList()
  3406. {
  3407. // Fonts previously in getFontListSans() have moved to fonts.xml.
  3408. return std::vector<std::string>();
  3409. }
  3410. #endif // LL_WINDOWS