llwindowmacosx.cpp 45 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910
  1. /**
  2. * @file llwindowmacosx.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 <OpenGL/OpenGL.h>
  34. #include <CoreServices/CoreServices.h>
  35. #include "llwindowmacosx.h"
  36. #include "indra_constants.h"
  37. #include "lldir.h"
  38. #include "llfasttimer.h"
  39. #include "llgl.h"
  40. #include "llkeyboardmacosx.h"
  41. #include "llpreeditor.h"
  42. #include "llrender.h" // For LLRender::sGLCoreProfile
  43. #include "llstring.h"
  44. #include "llwindowmacosx-objc.h"
  45. constexpr S32 BITS_PER_PIXEL = 32;
  46. constexpr S32 MAX_NUM_RESOLUTIONS = 32;
  47. namespace
  48. {
  49. NSKeyEventRef mRawKeyEvent = NULL;
  50. }
  51. //
  52. // LLWindowMacOSX
  53. //
  54. bool LLWindowMacOSX::sUseMultGL = false;
  55. // Cross-platform bits:
  56. bool check_for_card(const char* RENDERER, const char* bad_card)
  57. {
  58. if (!strnicmp(RENDERER, bad_card, strlen(bad_card)))
  59. {
  60. std::string buffer = llformat(
  61. "Your video card appears to be a %s, which Second Life does not support.\n"
  62. "\n"
  63. "Second Life requires a video card with 32 Mb of memory or more, as well as\n"
  64. "multitexture support. We explicitly support nVidia GeForce 2 or better, \n"
  65. "and ATI Radeon 8500 or better.\n"
  66. "\n"
  67. "If you own a supported card and continue to receive this message, try \n"
  68. "updating to the latest video card drivers. Otherwise look in the\n"
  69. "secondlife.com support section or e-mail technical support\n"
  70. "\n"
  71. "You can try to run Second Life, but it will probably crash or run\n"
  72. "very slowly. Try anyway?",
  73. bad_card);
  74. S32 button = OSMessageBox(buffer.c_str(), "Unsupported video card",
  75. OSMB_YESNO);
  76. return button != OSBTN_YES;
  77. }
  78. return false;
  79. }
  80. // Get a long value from a dictionary
  81. static long getDictLong(CFDictionaryRef refDict, CFStringRef key)
  82. {
  83. CFNumberRef number_value = (CFNumberRef)CFDictionaryGetValue(refDict, key);
  84. if (!number_value)
  85. {
  86. // if can't get a number for the dictionary
  87. return -1; // fail
  88. }
  89. long int_value;
  90. if (!CFNumberGetValue(number_value, kCFNumberLongType, &int_value))
  91. {
  92. // or if cant convert it
  93. return -1; // fail
  94. }
  95. return int_value; // otherwise return the long value
  96. }
  97. // *HACK: on the Mac, to put up an OS dialog in full screen mode, we must first
  98. // switch OUT of full screen mode. The proper way to do this is to bracket the
  99. // dialog with calls to beforeDialog() and afterDialog(), but these require a
  100. // pointer to the LLWindowMacOSX object. Stash it here and maintain in the
  101. // constructor and destructor. This assumes that there will be only one object
  102. // of this class at any time, which is currently the case.
  103. static LLWindowMacOSX* sWindowImplementation = NULL;
  104. LLWindowMacOSX::LLWindowMacOSX(const std::string& title, U32 flags,
  105. bool fullscreen, bool disable_vsync,
  106. U32 fsaa_samples)
  107. : LLWindow(fullscreen, flags)
  108. {
  109. setupCocoa();
  110. // Initialize the keyboard
  111. gKeyboardp = new LLKeyboardMacOSX();
  112. mWindow = NULL;
  113. mContext = NULL;
  114. mPixelFormat = NULL;
  115. mDisplay = CGMainDisplayID();
  116. mSimulatedRightClick = false;
  117. mLastModifiers = 0;
  118. mCursorDecoupled = false;
  119. mCursorLastEventDeltaX = 0;
  120. mCursorLastEventDeltaY = 0;
  121. mCursorIgnoreNextDelta = false;
  122. mMinimized = false;
  123. mLanguageTextInputAllowed = false;
  124. mPreeditor = NULL;
  125. mFSAASamples = fsaa_samples;
  126. mForceRebuild = false;
  127. // Get the original aspect ratio of the main device.
  128. mOriginalAspectRatio = (double)CGDisplayPixelsWide(mDisplay) /
  129. (double)CGDisplayPixelsHigh(mDisplay);
  130. // Stash the window title
  131. mWindowTitle = title;
  132. // Stash an object pointer for OSMessageBox()
  133. sWindowImplementation = this;
  134. // Create the GL context and set it up for windowed or fullscreen, as
  135. // appropriate.
  136. if (createContext(fullscreen, disable_vsync))
  137. {
  138. if (mWindow)
  139. {
  140. makeWindowOrderFront(mWindow);
  141. }
  142. if (!gGLManager.initGL())
  143. {
  144. setupFailure(
  145. "Second Life is unable to run because your video card drivers\n"
  146. "are out of date or unsupported. Please make sure you have\n"
  147. "the latest video card drivers installed.\n"
  148. "If you continue to receive this message, contact customer service.");
  149. return;
  150. }
  151. // Start with arrow cursor
  152. initCursors();
  153. setCursor(UI_CURSOR_ARROW);
  154. allowLanguageTextInput(NULL, false);
  155. }
  156. stop_glerror();
  157. }
  158. void LLWindowMacOSX::setWindowTitle(const std::string& title)
  159. {
  160. // Remember the new title, for when we switch context
  161. mWindowTitle = title;
  162. setWinTitle(mWindow, title);
  163. }
  164. bool LLWindowMacOSX::createContext(bool fullscreen, bool disable_vsync)
  165. {
  166. mFullscreen = fullscreen;
  167. if (!mWindow)
  168. {
  169. mWindow = getMainAppWindow();
  170. }
  171. if (!mContext)
  172. {
  173. // Our OpenGL view is already defined within CoolVLViewer.xib.
  174. // Get the view instead.
  175. mGLView = createOpenGLView(mWindow, mFSAASamples, !disable_vsync,
  176. LLRender::sGLCoreProfile);
  177. mContext = getCGLContextObj(mGLView);
  178. gGLManager.mVRAM = getVramSize(mGLView);
  179. if (!mPixelFormat)
  180. {
  181. CGLPixelFormatAttribute attribs[] =
  182. {
  183. kCGLPFANoRecovery,
  184. kCGLPFADoubleBuffer,
  185. kCGLPFAClosestPolicy,
  186. kCGLPFAAccelerated,
  187. kCGLPFAMultisample,
  188. kCGLPFASampleBuffers, CGLPixelFormatAttribute(mFSAASamples ? 1
  189. : 0),
  190. kCGLPFASamples, CGLPixelFormatAttribute(mFSAASamples),
  191. kCGLPFAStencilSize, CGLPixelFormatAttribute(8),
  192. kCGLPFADepthSize, CGLPixelFormatAttribute(24),
  193. kCGLPFAAlphaSize, CGLPixelFormatAttribute(8),
  194. kCGLPFAColorSize, CGLPixelFormatAttribute(24),
  195. CGLPixelFormatAttribute(0)
  196. };
  197. GLint num_formats;
  198. CGLChoosePixelFormat(attribs, &mPixelFormat, &num_formats);
  199. if (!mPixelFormat)
  200. {
  201. // Try again
  202. CGLChoosePixelFormat(attribs, &mPixelFormat, &num_formats);
  203. }
  204. }
  205. }
  206. // This sets up our view to receive text from our non-inline text input
  207. // window.
  208. setupInputWindow(mWindow, mGLView);
  209. if (mContext)
  210. {
  211. LL_DEBUGS("Window") << "Setting current context" << LL_ENDL;
  212. U32 err = CGLSetCurrentContext(mContext);
  213. if (err != kCGLNoError)
  214. {
  215. setupFailure("Cannot activate GL rendering context");
  216. return false;
  217. }
  218. }
  219. // Disable vertical sync for swap
  220. GLint frames_per_swap = disable_vsync ? 0 : 1;
  221. CGLSetParameter(mContext, kCGLCPSwapInterval, &frames_per_swap);
  222. // Enable multi-threaded OpenGL when configured to do so.
  223. if (sUseMultGL)
  224. {
  225. CGLError cgl_err;
  226. CGLContextObj ctx = CGLGetCurrentContext();
  227. cgl_err = CGLEnable(ctx, kCGLCEMPEngine);
  228. if (cgl_err != kCGLNoError)
  229. {
  230. LL_DEBUGS("GLInit") << "Multi-threaded OpenGL not available."
  231. << LL_ENDL;
  232. }
  233. else
  234. {
  235. LL_DEBUGS("GLInit") << "Multi-threaded OpenGL enabled." << LL_ENDL;
  236. }
  237. }
  238. makeFirstResponder(mWindow, mGLView);
  239. return true;
  240. }
  241. // We only support OS X 10.7's fullscreen app mode which is literally a full
  242. // screen window that fills a virtual desktop. This makes this method obsolete.
  243. bool LLWindowMacOSX::switchContext(bool, const LLCoordScreen&, bool,
  244. const LLCoordScreen* const)
  245. {
  246. return false;
  247. }
  248. void LLWindowMacOSX::destroyContext()
  249. {
  250. if (!mContext)
  251. {
  252. // We do not have a context
  253. return;
  254. }
  255. // Unhook the GL context from any drawable it may have
  256. LL_DEBUGS("Window") << "Unhooking drawable" << LL_ENDL;
  257. CGLSetCurrentContext(NULL);
  258. // Clean up remaining GL state before blowing away window
  259. gGLManager.shutdownGL();
  260. // Clean up the pixel format
  261. if (mPixelFormat)
  262. {
  263. LL_DEBUGS("Window") << "Destroying pixel format" << LL_ENDL;
  264. CGLDestroyPixelFormat(mPixelFormat);
  265. mPixelFormat = NULL;
  266. }
  267. LL_DEBUGS("Window") << "Destroying context" << LL_ENDL;
  268. CGLDestroyContext(mContext);
  269. #if 1
  270. mContext = NULL;
  271. #endif
  272. // Destroy our LLOpenGLView
  273. if (mGLView)
  274. {
  275. LL_DEBUGS("Window") << "Destroying GL view" << LL_ENDL;
  276. removeGLView(mGLView);
  277. mGLView = NULL;
  278. }
  279. // Close the window
  280. if (mWindow)
  281. {
  282. LL_DEBUGS("Window") << "Disposing window" << LL_ENDL;
  283. NSWindowRef dead_window = mWindow;
  284. mWindow = NULL;
  285. closeWindow(dead_window);
  286. }
  287. }
  288. LLWindowMacOSX::~LLWindowMacOSX()
  289. {
  290. destroyContext();
  291. if (mSupportedResolutions)
  292. {
  293. delete[] mSupportedResolutions;
  294. }
  295. sWindowImplementation = NULL;
  296. }
  297. void LLWindowMacOSX::show()
  298. {
  299. }
  300. void LLWindowMacOSX::hide()
  301. {
  302. setMouseClipping(false);
  303. }
  304. void LLWindowMacOSX::minimize()
  305. {
  306. setMouseClipping(false);
  307. showCursor();
  308. }
  309. void LLWindowMacOSX::restore()
  310. {
  311. show();
  312. }
  313. // Destroys all OS-specific code associated with a window. Usually called from
  314. // LLWindow::destroyWindow()
  315. void LLWindowMacOSX::close()
  316. {
  317. #if 0
  318. // Is window is already closed ?
  319. if (!mWindow)
  320. {
  321. return;
  322. }
  323. #endif
  324. // Make sure cursor is visible and we have not mangled the clipping state.
  325. setMouseClipping(false);
  326. showCursor();
  327. destroyContext();
  328. }
  329. bool LLWindowMacOSX::isValid()
  330. {
  331. return mFullscreen || mWindow != NULL;
  332. }
  333. bool LLWindowMacOSX::getVisible()
  334. {
  335. return mFullscreen || mWindow != NULL;
  336. }
  337. bool LLWindowMacOSX::getMinimized()
  338. {
  339. return mMinimized;
  340. }
  341. void LLWindowMacOSX::gatherInput()
  342. {
  343. updateCursor();
  344. }
  345. bool LLWindowMacOSX::getPosition(LLCoordScreen* position)
  346. {
  347. if (mFullscreen)
  348. {
  349. position->mX = 0;
  350. position->mY = 0;
  351. return true;
  352. }
  353. else if (mWindow)
  354. {
  355. const CGPoint& pos = getContentViewBoundsPosition(mWindow);
  356. position->mX = pos.x;
  357. position->mY = pos.y;
  358. return true;
  359. }
  360. llerrs << "No window and not fullscreen !" << llendl;
  361. return false;
  362. }
  363. bool LLWindowMacOSX::getSize(LLCoordScreen* size)
  364. {
  365. if (mFullscreen)
  366. {
  367. size->mX = mFullscreenWidth;
  368. size->mY = mFullscreenHeight;
  369. return true;
  370. }
  371. else if (mWindow)
  372. {
  373. const CGSize& sz = gHiDPISupport ? getDeviceContentViewSize(mWindow,
  374. mGLView)
  375. : getContentViewBoundsSize(mWindow);
  376. size->mX = sz.width;
  377. size->mY = sz.height;
  378. return true;
  379. }
  380. llerrs << "No window and not fullscreen !" << llendl;
  381. return false;
  382. }
  383. bool LLWindowMacOSX::getSize(LLCoordWindow* size)
  384. {
  385. if (mFullscreen)
  386. {
  387. size->mX = mFullscreenWidth;
  388. size->mY = mFullscreenHeight;
  389. return true;
  390. }
  391. else if (mWindow)
  392. {
  393. const CGSize& sz = gHiDPISupport ? getDeviceContentViewSize(mWindow,
  394. mGLView)
  395. : getContentViewBoundsSize(mWindow);
  396. size->mX = sz.width;
  397. size->mY = sz.height;
  398. return true;
  399. }
  400. llerrs << "No window and not fullscreen !" << llendl;
  401. return false;
  402. }
  403. bool LLWindowMacOSX::setPosition(const LLCoordScreen position)
  404. {
  405. if (mWindow)
  406. {
  407. float pos[2] = { (float)position.mX, (float)position.mY };
  408. setWindowPos(mWindow, pos);
  409. return true;
  410. }
  411. return false;
  412. }
  413. bool LLWindowMacOSX::setSize(const LLCoordScreen size)
  414. {
  415. if (mWindow)
  416. {
  417. LLCoordWindow to;
  418. convertCoords(size, &to);
  419. setWindowSize(mWindow, to.mX, to.mY);
  420. return true;
  421. }
  422. return false;
  423. }
  424. void LLWindowMacOSX::swapBuffers()
  425. {
  426. LL_FAST_TIMER(FTM_SWAP);
  427. CGLFlushDrawable(mContext);
  428. }
  429. void LLWindowMacOSX::setFSAASamples(U32 samples)
  430. {
  431. mFSAASamples = samples;
  432. mForceRebuild = true;
  433. }
  434. bool LLWindowMacOSX::restoreGamma()
  435. {
  436. CGDisplayRestoreColorSyncSettings();
  437. return true;
  438. }
  439. #if 0
  440. F32 LLWindowMacOSX::getGamma()
  441. {
  442. F32 result = 1.f; // Default to something sane
  443. CGGammaValue rmin, rmax, rgamma, gmin, gmax, ggamma, bmin, bmax, bgamma;
  444. if (CGGetDisplayTransferByFormula(mDisplay, &rmin, &rmax, &rgamma,
  445. &gmin, &gmax, &ggamma,
  446. &bmin, &bmax, &bgamma) == noErr)
  447. {
  448. F32 sum = rgamma + ggamma + bgamma;
  449. if (sum > 0.f)
  450. {
  451. result = 3.f / sum;
  452. }
  453. }
  454. return result;
  455. }
  456. #endif
  457. // Should we allow this in windowed mode ?
  458. bool LLWindowMacOSX::setGamma(F32 gamma)
  459. {
  460. mCurrentGamma = llclamp(gamma, 0.01f, 10.f);
  461. LL_DEBUGS("Window") << "Setting gamma to " << mCurrentGamma << LL_ENDL;
  462. CGGammaValue rmin, rmax, rgamma, gmin, gmax, ggamma, bmin, bmax, bgamma;
  463. if (CGGetDisplayTransferByFormula(mDisplay,
  464. &rmin, &rmax, &rgamma,
  465. &gmin, &gmax, &ggamma,
  466. &bmin, &bmax, &bgamma) != noErr)
  467. {
  468. return false;
  469. }
  470. F32 inv_gamma = 1.f / mCurrentGamma;
  471. return CGSetDisplayTransferByFormula(mDisplay,
  472. rmin, rmax, inv_gamma,
  473. gmin, gmax, inv_gamma,
  474. bmin, bmax, inv_gamma) == noErr;
  475. }
  476. // Constrains the mouse to the window.
  477. void LLWindowMacOSX::setMouseClipping(bool b)
  478. {
  479. // Just stash the requested state. We will simulate this when the cursor is
  480. // hidden by decoupling.
  481. mIsMouseClipping = b;
  482. adjustCursorDecouple();
  483. }
  484. bool LLWindowMacOSX::setCursorPosition(const LLCoordWindow& position)
  485. {
  486. LLCoordScreen screen_pos;
  487. if (!mCallbacks || !convertCoords(position, &screen_pos))
  488. {
  489. return false;
  490. }
  491. CGPoint new_pos;
  492. new_pos.x = screen_pos.mX;
  493. new_pos.y = screen_pos.mY;
  494. CGSetLocalEventsSuppressionInterval(0.0);
  495. bool result = CGWarpMouseCursorPosition(new_pos) == noErr;
  496. // Under certain circumstances, this will trigger us to decouple the cursor
  497. adjustCursorDecouple(true);
  498. // Trigger mouse move callback
  499. LLCoordGL gl_pos;
  500. convertCoords(position, &gl_pos);
  501. F32 scale = getSystemUISize();
  502. gl_pos.mX *= scale;
  503. gl_pos.mY *= scale;
  504. mCallbacks->handleMouseMove(this, gl_pos, (MASK)0);
  505. return result;
  506. }
  507. bool LLWindowMacOSX::getCursorPosition(LLCoordWindow* position)
  508. {
  509. if (!mWindow || !position)
  510. {
  511. return false;
  512. }
  513. float cursor_point[2];
  514. getCursorPos(mWindow, cursor_point);
  515. if (mCursorDecoupled)
  516. {
  517. cursor_point[0] += mCursorLastEventDeltaX;
  518. cursor_point[1] += mCursorLastEventDeltaY;
  519. }
  520. F32 scale = getSystemUISize();
  521. position->mX = cursor_point[0] * scale;
  522. position->mY = cursor_point[1] * scale;
  523. return true;
  524. }
  525. void LLWindowMacOSX::adjustCursorDecouple(bool warping_mouse)
  526. {
  527. if (mIsMouseClipping && mCursorHidden)
  528. {
  529. if (warping_mouse)
  530. {
  531. // The cursor should be decoupled. Make sure it is.
  532. if (!mCursorDecoupled)
  533. {
  534. LL_DEBUGS("Window") << "Decoupling cursor" << LL_ENDL;
  535. CGAssociateMouseAndMouseCursorPosition(false);
  536. mCursorDecoupled = true;
  537. mCursorIgnoreNextDelta = true;
  538. }
  539. }
  540. }
  541. else
  542. {
  543. // The cursor should not be decoupled. Make sure it is not.
  544. if (mCursorDecoupled)
  545. {
  546. LL_DEBUGS("Window") << "Re-coupling cursor" << LL_ENDL;
  547. CGAssociateMouseAndMouseCursorPosition(true);
  548. mCursorDecoupled = false;
  549. }
  550. }
  551. }
  552. F32 LLWindowMacOSX::getNativeAspectRatio()
  553. {
  554. if (mFullscreen)
  555. {
  556. return (F32)mFullscreenWidth / (F32)mFullscreenHeight;
  557. }
  558. else
  559. {
  560. // The constructor for this class grabs the aspect ratio of the monitor
  561. // before doing any resolution switching, and stashes it in
  562. // mOriginalAspectRatio. Here, we just return it.
  563. if (mOverrideAspectRatio > 0.f)
  564. {
  565. return mOverrideAspectRatio;
  566. }
  567. return mOriginalAspectRatio;
  568. }
  569. }
  570. F32 LLWindowMacOSX::getPixelAspectRatio()
  571. {
  572. // OS X always enforces a 1:1 pixel aspect ratio, regardless of video mode
  573. return 1.f;
  574. }
  575. // Since we are no longer supporting the "typical" fullscreen mode with CGL or
  576. // NSOpenGL anymore, these are unnecessary. -Geenz
  577. void LLWindowMacOSX::beforeDialog()
  578. {
  579. }
  580. void LLWindowMacOSX::afterDialog()
  581. {
  582. // Fixes crash with Core Flow view on OS-X
  583. CGLSetCurrentContext(mContext);
  584. }
  585. void LLWindowMacOSX::flashIcon(F32)
  586. {
  587. // For consistency with OS X conventions, the number of seconds given is
  588. // ignored and left up to the OS (which will actually bounce it for one
  589. // second).
  590. requestUserAttention();
  591. }
  592. bool LLWindowMacOSX::isClipboardTextAvailable()
  593. {
  594. return pasteBoardAvailable();
  595. }
  596. bool LLWindowMacOSX::pasteTextFromClipboard(LLWString& dst)
  597. {
  598. llutf16string str(copyFromPBoard());
  599. dst = utf16str_to_wstring(str);
  600. // *HACK: there is (sometimes) a spurious 'nul' character appearing at the
  601. // end of the string returned by copyFromPBoard()... So, let's remove it.
  602. size_t len = dst.size();
  603. if (len > 1 && dst[len - 1] == '\0')
  604. {
  605. dst = dst.substr(0, len - 1);
  606. }
  607. return dst != L"";
  608. }
  609. bool LLWindowMacOSX::copyTextToClipboard(const LLWString& s)
  610. {
  611. llutf16string utf16str = wstring_to_utf16str(s);
  612. return copyToPBoard(utf16str.data(), utf16str.length());
  613. }
  614. //virtual
  615. bool LLWindowMacOSX::isPrimaryTextAvailable()
  616. {
  617. return !mPrimaryClipboard.empty();
  618. }
  619. //virtual
  620. bool LLWindowMacOSX::pasteTextFromPrimary(LLWString& text)
  621. {
  622. if (mPrimaryClipboard.empty())
  623. {
  624. return false;
  625. }
  626. text = mPrimaryClipboard;
  627. return true;
  628. }
  629. // virtual
  630. bool LLWindowMacOSX::copyTextToPrimary(const LLWString& text)
  631. {
  632. mPrimaryClipboard = text;
  633. return true;
  634. }
  635. LLWindow::LLWindowResolution* LLWindowMacOSX::getSupportedResolutions(S32& num_resolutions)
  636. {
  637. if (mSupportedResolutions)
  638. {
  639. num_resolutions = mNumSupportedResolutions;
  640. return mSupportedResolutions;
  641. }
  642. CFArrayRef modes = CGDisplayAvailableModes(mDisplay);
  643. if (modes)
  644. {
  645. mSupportedResolutions = new LLWindowResolution[MAX_NUM_RESOLUTIONS];
  646. mNumSupportedResolutions = 0;
  647. // Examine each mode
  648. CFIndex cnt = CFArrayGetCount(modes);
  649. for (CFIndex index = 0;
  650. index < cnt && mNumSupportedResolutions < MAX_NUM_RESOLUTIONS;
  651. ++index)
  652. {
  653. // Pull the mode dictionary out of the CFArray
  654. CFDictionaryRef mode =
  655. (CFDictionaryRef)CFArrayGetValueAtIndex(modes, index);
  656. S32 w = getDictLong(mode, kCGDisplayWidth);
  657. S32 h = getDictLong(mode, kCGDisplayHeight);
  658. S32 bits = getDictLong(mode, kCGDisplayBitsPerPixel);
  659. if (bits == BITS_PER_PIXEL && w >= 800 && h >= 600)
  660. {
  661. bool resolution_exists = false;
  662. for (S32 i = 0; i < mNumSupportedResolutions; ++i)
  663. {
  664. if (mSupportedResolutions[i].mWidth == w &&
  665. mSupportedResolutions[i].mHeight == h)
  666. {
  667. resolution_exists = true;
  668. break;
  669. }
  670. }
  671. if (!resolution_exists)
  672. {
  673. mSupportedResolutions[mNumSupportedResolutions].mWidth = w;
  674. mSupportedResolutions[mNumSupportedResolutions++].mHeight = h;
  675. }
  676. }
  677. }
  678. }
  679. num_resolutions = mNumSupportedResolutions;
  680. return mSupportedResolutions;
  681. }
  682. bool LLWindowMacOSX::convertCoords(LLCoordGL from, LLCoordWindow* to)
  683. {
  684. to->mX = from.mX;
  685. to->mY = from.mY;
  686. return true;
  687. }
  688. bool LLWindowMacOSX::convertCoords(LLCoordWindow from, LLCoordGL* to)
  689. {
  690. to->mX = from.mX;
  691. to->mY = from.mY;
  692. return true;
  693. }
  694. bool LLWindowMacOSX::convertCoords(LLCoordScreen from, LLCoordWindow* to)
  695. {
  696. if (mWindow && to)
  697. {
  698. float mouse_point[2];
  699. mouse_point[0] = from.mX;
  700. mouse_point[1] = from.mY;
  701. convertScreenToWindow(mWindow, mouse_point);
  702. to->mX = mouse_point[0];
  703. to->mY = mouse_point[1];
  704. return true;
  705. }
  706. return false;
  707. }
  708. bool LLWindowMacOSX::convertCoords(LLCoordWindow from, LLCoordScreen* to)
  709. {
  710. if (mWindow && to)
  711. {
  712. float mouse_point[2];
  713. mouse_point[0] = from.mX;
  714. mouse_point[1] = from.mY;
  715. convertWindowToScreen(mWindow, mouse_point);
  716. to->mX = mouse_point[0];
  717. to->mY = mouse_point[1];
  718. return true;
  719. }
  720. return false;
  721. }
  722. bool LLWindowMacOSX::convertCoords(LLCoordScreen from, LLCoordGL* to)
  723. {
  724. LLCoordWindow window_coord;
  725. return convertCoords(from, &window_coord) &&
  726. convertCoords(window_coord, to);
  727. }
  728. bool LLWindowMacOSX::convertCoords(LLCoordGL from, LLCoordScreen* to)
  729. {
  730. LLCoordWindow window_coord;
  731. return convertCoords(from, &window_coord) &&
  732. convertCoords(window_coord, to);
  733. }
  734. void LLWindowMacOSX::setupFailure(const std::string& text)
  735. {
  736. destroyContext();
  737. OSMessageBox(text);
  738. }
  739. static const char* cursorIDToName(int id)
  740. {
  741. switch (id)
  742. {
  743. case UI_CURSOR_ARROW: return "ui_cursor_arrow";
  744. case UI_CURSOR_WAIT: return "ui_cursor_wait";
  745. case UI_CURSOR_HAND: return "ui_cursor_hand";
  746. case UI_CURSOR_IBEAM: return "ui_cursor_ibeam";
  747. case UI_CURSOR_CROSS: return "ui_cursor_cross";
  748. case UI_CURSOR_SIZENWSE: return "ui_cursor_sizenwse";
  749. case UI_CURSOR_SIZENESW: return "ui_cursor_sizenesw";
  750. case UI_CURSOR_SIZEWE: return "ui_cursor_sizewe";
  751. case UI_CURSOR_SIZENS: return "ui_cursor_sizens";
  752. case UI_CURSOR_NO: return "ui_cursor_no";
  753. case UI_CURSOR_WORKING: return "ui_cursor_working";
  754. case UI_CURSOR_TOOLGRAB: return "ui_cursor_toolgrab";
  755. case UI_CURSOR_TOOLLAND: return "ui_cursor_toolland";
  756. case UI_CURSOR_TOOLFOCUS: return "ui_cursor_toolfocus";
  757. case UI_CURSOR_TOOLCREATE: return "ui_cursor_toolcreate";
  758. case UI_CURSOR_ARROWDRAG: return "ui_cursor_arrowdrag";
  759. case UI_CURSOR_ARROWCOPY: return "ui_cursor_arrowcopy";
  760. case UI_CURSOR_ARROWDRAGMULTI: return "ui_cursor_arrowdragmulti";
  761. case UI_CURSOR_ARROWCOPYMULTI: return "ui_cursor_arrowcopymulti";
  762. case UI_CURSOR_NOLOCKED: return "ui_cursor_nolocked";
  763. case UI_CURSOR_ARROWLOCKED: return "ui_cursor_arrowlocked";
  764. case UI_CURSOR_GRABLOCKED: return "ui_cursor_grablocked";
  765. case UI_CURSOR_TOOLTRANSLATE: return "ui_cursor_tooltranslate";
  766. case UI_CURSOR_TOOLROTATE: return "ui_cursor_toolrotate";
  767. case UI_CURSOR_TOOLSCALE: return "ui_cursor_toolscale";
  768. case UI_CURSOR_TOOLCAMERA: return "ui_cursor_toolcamera";
  769. case UI_CURSOR_TOOLPAN: return "ui_cursor_toolpan";
  770. case UI_CURSOR_TOOLZOOMIN: return "ui_cursor_toolzoomin";
  771. case UI_CURSOR_TOOLPICKOBJECT3: return "ui_cursor_toolpickobject3";
  772. case UI_CURSOR_TOOLSIT: return "ui_cursor_toolsit";
  773. case UI_CURSOR_TOOLBUY: return "ui_cursor_toolbuy";
  774. case UI_CURSOR_TOOLPAY: return "ui_cursor_toolpay";
  775. case UI_CURSOR_TOOLOPEN: return "ui_cursor_toolopen";
  776. case UI_CURSOR_TOOLPLAY: return "ui_cursor_toolplay";
  777. case UI_CURSOR_TOOLPAUSE: return "ui_cursor_toolpause";
  778. case UI_CURSOR_TOOLMEDIAOPEN: return "ui_cursor_toolmediaopen";
  779. case UI_CURSOR_PIPETTE: return "ui_cursor_pipette";
  780. case UI_CURSOR_TOOLPATHFINDING: return "ui_cursor_pathfinding";
  781. case UI_CURSOR_TOOLPATHFINDING_PATH_START: return "ui_cursor_pathfinding_start";
  782. case UI_CURSOR_TOOLPATHFINDING_PATH_START_ADD: return "ui_cursor_pathfinding_start_add";
  783. case UI_CURSOR_TOOLPATHFINDING_PATH_END: return "ui_cursor_pathfinding_end";
  784. case UI_CURSOR_TOOLPATHFINDING_PATH_END_ADD: return "ui_cursor_pathfinding_end_add";
  785. case UI_CURSOR_TOOLNO: return "ui_cursor_no";
  786. }
  787. llerrs << "Unknown cursor id: " << id << llendl;
  788. return "ui_cursor_arrow";
  789. }
  790. static CursorRef gCursors[UI_CURSOR_COUNT];
  791. static void initPixmapCursor(int cursorid, int hotspot_x, int hotspot_y)
  792. {
  793. // Cursors are in:
  794. // <Application Bundle>/Contents/Resources/cursors_mac/ui_cursor_foo.tif
  795. std::string fullpath = gDirUtil.getAppRODataDir() + "/cursors_mac/";
  796. fullpath += cursorIDToName(cursorid);
  797. fullpath += ".tif";
  798. gCursors[cursorid] = createImageCursor(fullpath.c_str(), hotspot_x,
  799. hotspot_y);
  800. }
  801. void LLWindowMacOSX::updateCursor()
  802. {
  803. if (mNextCursor == UI_CURSOR_ARROW && mBusyCount > 0)
  804. {
  805. mNextCursor = UI_CURSOR_WORKING;
  806. }
  807. if (mCurrentCursor == mNextCursor)
  808. {
  809. if (mCursorHidden && mHideCursorPermanent && isCGCursorVisible())
  810. {
  811. hideNSCursor();
  812. adjustCursorDecouple();
  813. }
  814. return;
  815. }
  816. // RN: replace multi-drag cursors with single versions
  817. if (mNextCursor == UI_CURSOR_ARROWDRAGMULTI)
  818. {
  819. mNextCursor = UI_CURSOR_ARROWDRAG;
  820. }
  821. else if (mNextCursor == UI_CURSOR_ARROWCOPYMULTI)
  822. {
  823. mNextCursor = UI_CURSOR_ARROWCOPY;
  824. }
  825. switch (mNextCursor)
  826. {
  827. default:
  828. case UI_CURSOR_ARROW:
  829. setArrowCursor();
  830. if (mCursorHidden)
  831. {
  832. // Since InitCursor resets the hide level, correct for it here.
  833. hideNSCursor();
  834. }
  835. break;
  836. // Some of the standard Windows cursors have no standard Mac
  837. // equivalents. Find out what they look like and replicate them.
  838. // These are essentially correct
  839. case UI_CURSOR_WAIT:
  840. // Apple purposely does not allow us to set the beachball cursor
  841. // manually. Let NSApp figure out when to do this.
  842. break;
  843. case UI_CURSOR_IBEAM:
  844. setIBeamCursor();
  845. break;
  846. case UI_CURSOR_CROSS:
  847. setCrossCursor();
  848. break;
  849. case UI_CURSOR_HAND:
  850. setPointingHandCursor();
  851. break;
  852. case UI_CURSOR_ARROWCOPY:
  853. setCopyCursor();
  854. break;
  855. // Double-check these
  856. case UI_CURSOR_NO:
  857. case UI_CURSOR_SIZEWE:
  858. case UI_CURSOR_SIZENS:
  859. case UI_CURSOR_SIZENWSE:
  860. case UI_CURSOR_SIZENESW:
  861. case UI_CURSOR_WORKING:
  862. case UI_CURSOR_TOOLGRAB:
  863. case UI_CURSOR_TOOLLAND:
  864. case UI_CURSOR_TOOLFOCUS:
  865. case UI_CURSOR_TOOLCREATE:
  866. case UI_CURSOR_ARROWDRAG:
  867. case UI_CURSOR_NOLOCKED:
  868. case UI_CURSOR_ARROWLOCKED:
  869. case UI_CURSOR_GRABLOCKED:
  870. case UI_CURSOR_TOOLTRANSLATE:
  871. case UI_CURSOR_TOOLROTATE:
  872. case UI_CURSOR_TOOLSCALE:
  873. case UI_CURSOR_TOOLCAMERA:
  874. case UI_CURSOR_TOOLPAN:
  875. case UI_CURSOR_TOOLZOOMIN:
  876. case UI_CURSOR_TOOLPICKOBJECT3:
  877. case UI_CURSOR_TOOLPLAY:
  878. case UI_CURSOR_TOOLPAUSE:
  879. case UI_CURSOR_TOOLMEDIAOPEN:
  880. case UI_CURSOR_TOOLSIT:
  881. case UI_CURSOR_TOOLBUY:
  882. case UI_CURSOR_TOOLPAY:
  883. case UI_CURSOR_TOOLOPEN:
  884. case UI_CURSOR_TOOLPATHFINDING:
  885. case UI_CURSOR_TOOLPATHFINDING_PATH_START:
  886. case UI_CURSOR_TOOLPATHFINDING_PATH_START_ADD:
  887. case UI_CURSOR_TOOLPATHFINDING_PATH_END:
  888. case UI_CURSOR_TOOLPATHFINDING_PATH_END_ADD:
  889. case UI_CURSOR_TOOLNO:
  890. {
  891. if (setImageCursor(gCursors[mNextCursor]) != noErr)
  892. {
  893. setArrowCursor();
  894. }
  895. }
  896. }
  897. mCurrentCursor = mNextCursor;
  898. }
  899. void LLWindowMacOSX::initCursors()
  900. {
  901. initPixmapCursor(UI_CURSOR_NO, 8, 8);
  902. initPixmapCursor(UI_CURSOR_WORKING, 1, 1);
  903. initPixmapCursor(UI_CURSOR_TOOLGRAB, 2, 14);
  904. initPixmapCursor(UI_CURSOR_TOOLLAND, 13, 8);
  905. initPixmapCursor(UI_CURSOR_TOOLFOCUS, 7, 6);
  906. initPixmapCursor(UI_CURSOR_TOOLCREATE, 7, 7);
  907. initPixmapCursor(UI_CURSOR_ARROWDRAG, 1, 1);
  908. initPixmapCursor(UI_CURSOR_ARROWCOPY, 1, 1);
  909. initPixmapCursor(UI_CURSOR_NOLOCKED, 8, 8);
  910. initPixmapCursor(UI_CURSOR_ARROWLOCKED, 1, 1);
  911. initPixmapCursor(UI_CURSOR_GRABLOCKED, 2, 14);
  912. initPixmapCursor(UI_CURSOR_TOOLTRANSLATE, 1, 1);
  913. initPixmapCursor(UI_CURSOR_TOOLROTATE, 1, 1);
  914. initPixmapCursor(UI_CURSOR_TOOLSCALE, 1, 1);
  915. initPixmapCursor(UI_CURSOR_TOOLCAMERA, 7, 6);
  916. initPixmapCursor(UI_CURSOR_TOOLPAN, 7, 6);
  917. initPixmapCursor(UI_CURSOR_TOOLZOOMIN, 7, 6);
  918. initPixmapCursor(UI_CURSOR_TOOLPICKOBJECT3, 1, 1);
  919. initPixmapCursor(UI_CURSOR_TOOLSIT, 1, 1);
  920. initPixmapCursor(UI_CURSOR_TOOLBUY, 1, 1);
  921. initPixmapCursor(UI_CURSOR_TOOLPAY, 1, 1);
  922. initPixmapCursor(UI_CURSOR_TOOLOPEN, 1, 1);
  923. initPixmapCursor(UI_CURSOR_TOOLPLAY, 1, 1);
  924. initPixmapCursor(UI_CURSOR_TOOLPAUSE, 1, 1);
  925. initPixmapCursor(UI_CURSOR_TOOLMEDIAOPEN, 1, 1);
  926. initPixmapCursor(UI_CURSOR_TOOLPATHFINDING, 16, 16);
  927. initPixmapCursor(UI_CURSOR_TOOLPATHFINDING_PATH_START, 16, 16);
  928. initPixmapCursor(UI_CURSOR_TOOLPATHFINDING_PATH_START_ADD, 16, 16);
  929. initPixmapCursor(UI_CURSOR_TOOLPATHFINDING_PATH_END, 16, 16);
  930. initPixmapCursor(UI_CURSOR_TOOLPATHFINDING_PATH_END_ADD, 16, 16);
  931. initPixmapCursor(UI_CURSOR_TOOLNO, 8, 8);
  932. initPixmapCursor(UI_CURSOR_SIZENWSE, 10, 10);
  933. initPixmapCursor(UI_CURSOR_SIZENESW, 10, 10);
  934. initPixmapCursor(UI_CURSOR_SIZEWE, 10, 10);
  935. initPixmapCursor(UI_CURSOR_SIZENS, 10, 10);
  936. }
  937. void LLWindowMacOSX::setCursor(ECursorType c)
  938. {
  939. if (!mCursorFrozen)
  940. {
  941. mNextCursor = c;
  942. }
  943. }
  944. void LLWindowMacOSX::captureMouse()
  945. {
  946. // By registering a global Event handler for mouse move events, we ensure
  947. // that mouse events are always processed. Thus, capture and release are
  948. // unnecessary.
  949. }
  950. void LLWindowMacOSX::releaseMouse()
  951. {
  952. // By registering a global Event handler for mouse move events, we ensure
  953. // that mouse events are always processed. Thus, capture and release are
  954. // unnecessary.
  955. }
  956. void LLWindowMacOSX::hideCursor()
  957. {
  958. if (!mCursorHidden)
  959. {
  960. mCursorHidden = mHideCursorPermanent = true;
  961. hideNSCursor();
  962. }
  963. adjustCursorDecouple();
  964. }
  965. void LLWindowMacOSX::showCursor()
  966. {
  967. if (mCursorHidden)
  968. {
  969. mCursorHidden = mHideCursorPermanent = false;
  970. showNSCursor();
  971. }
  972. adjustCursorDecouple();
  973. }
  974. void LLWindowMacOSX::showCursorFromMouseMove()
  975. {
  976. if (!mHideCursorPermanent)
  977. {
  978. showCursor();
  979. }
  980. }
  981. void LLWindowMacOSX::hideCursorUntilMouseMove()
  982. {
  983. if (!mHideCursorPermanent)
  984. {
  985. hideCursor();
  986. mHideCursorPermanent = false;
  987. }
  988. }
  989. //
  990. // LLSplashScreenMacOSX
  991. //
  992. LLSplashScreenMacOSX::LLSplashScreenMacOSX()
  993. {
  994. mWindow = NULL;
  995. }
  996. void LLSplashScreenMacOSX::showImpl()
  997. {
  998. // This _could_ be used to display a spash screen...
  999. }
  1000. void LLSplashScreenMacOSX::updateImpl(const std::string& mesg)
  1001. {
  1002. }
  1003. void LLSplashScreenMacOSX::hideImpl()
  1004. {
  1005. if (mWindow)
  1006. {
  1007. mWindow = NULL;
  1008. }
  1009. }
  1010. S32 OSMessageBoxMacOSX(const std::string& text, const std::string& caption,
  1011. U32 type)
  1012. {
  1013. return showAlert(text, caption, type);
  1014. }
  1015. // Open a URL with the user's default web browser. Must begin with protocol
  1016. // identifier.
  1017. void LLWindowMacOSX::spawnWebBrowser(const std::string& escaped_url,
  1018. bool async)
  1019. {
  1020. bool found = false;
  1021. for (S32 i = 0; i < gURLProtocolWhitelistCount; ++i)
  1022. {
  1023. if (escaped_url.find(gURLProtocolWhitelist[i]) != std::string::npos)
  1024. {
  1025. found = true;
  1026. break;
  1027. }
  1028. }
  1029. if (!found)
  1030. {
  1031. llwarns << "spawn_web_browser called for url with protocol not on whitelist: "
  1032. << escaped_url << llendl;
  1033. return;
  1034. }
  1035. S32 result = 0;
  1036. CFURLRef urlRef = NULL;
  1037. llinfos << "Opening URL " << escaped_url << llendl;
  1038. CFStringRef stringRef = CFStringCreateWithCString(NULL,
  1039. escaped_url.c_str(),
  1040. kCFStringEncodingUTF8);
  1041. if (stringRef)
  1042. {
  1043. // This will succeed if the string is a full URL, including the http://
  1044. // Note that URLs specified this way need to be properly percent-
  1045. // escaped.
  1046. urlRef = CFURLCreateWithString(NULL, stringRef, NULL);
  1047. // Do not use CRURLCreateWithFileSystemPath -- only want valid URLs
  1048. CFRelease(stringRef);
  1049. }
  1050. if (urlRef)
  1051. {
  1052. result = LSOpenCFURLRef(urlRef, NULL);
  1053. if (result != noErr)
  1054. {
  1055. llwarns << "Error " << result << " on open." << llendl;
  1056. }
  1057. CFRelease(urlRef);
  1058. }
  1059. else
  1060. {
  1061. llwarns << "Could not create URL." << llendl;
  1062. }
  1063. }
  1064. // Make the raw keyboard data available
  1065. LLSD LLWindowMacOSX::getNativeKeyData()
  1066. {
  1067. LLSD result = LLSD::emptyMap();
  1068. if (mRawKeyEvent)
  1069. {
  1070. result["event_type"] = LLSD::Integer(mRawKeyEvent->mEventType);
  1071. result["event_modifiers"] =
  1072. LLSD::Integer(mRawKeyEvent->mEventModifiers);
  1073. result["event_keycode"] = LLSD::Integer(mRawKeyEvent->mEventKeyCode);
  1074. result["event_chars"] =
  1075. mRawKeyEvent->mEventChars ? LLSD(LLSD::Integer(mRawKeyEvent->mEventChars))
  1076. : LLSD();
  1077. result["event_umodchars"] =
  1078. mRawKeyEvent->mEventUnmodChars ? LLSD(LLSD::Integer(mRawKeyEvent->mEventUnmodChars))
  1079. : LLSD();
  1080. result["event_isrepeat"] = LLSD::Boolean(mRawKeyEvent->mEventRepeat);
  1081. }
  1082. LL_DEBUGS("Window") << "Native key data is: " << result << LL_ENDL;
  1083. return result;
  1084. }
  1085. F32 LLWindowMacOSX::getSystemUISize()
  1086. {
  1087. return gHiDPISupport ? ::getDeviceUnitSize(mGLView) : 1.f;
  1088. }
  1089. void* LLWindowMacOSX::getPlatformWindow()
  1090. {
  1091. // NOTE: this will be NULL in fullscreen mode. Plan accordingly.
  1092. return (void*)mWindow;
  1093. }
  1094. void LLWindowMacOSX::allowLanguageTextInput(LLPreeditor* preeditor, bool b)
  1095. {
  1096. if (!preeditor) return;
  1097. if (preeditor != mPreeditor && !b)
  1098. {
  1099. // This condition may occur by a call to
  1100. // setEnabled(bool) against LLTextEditor or LLLineEditor
  1101. // when the control is not focused.
  1102. // We need to silently ignore the case so that
  1103. // the language input status of the focused control
  1104. // is not disturbed.
  1105. return;
  1106. }
  1107. // Take care of old and new preeditors.
  1108. if (preeditor != mPreeditor || !b)
  1109. {
  1110. // We need to interrupt before updating mPreeditor,
  1111. // so that the fix string from input method goes to
  1112. // the old preeditor.
  1113. if (mLanguageTextInputAllowed)
  1114. {
  1115. interruptLanguageTextInput();
  1116. }
  1117. mPreeditor = (b ? preeditor : NULL);
  1118. }
  1119. if (mLanguageTextInputAllowed != b)
  1120. {
  1121. mLanguageTextInputAllowed = b;
  1122. allowDirectMarkedTextInput(b, mGLView);
  1123. }
  1124. }
  1125. void LLWindowMacOSX::interruptLanguageTextInput()
  1126. {
  1127. commitCurrentPreedit(mGLView);
  1128. }
  1129. //static
  1130. std::vector<std::string> LLWindowMacOSX::getDynamicFallbackFontList()
  1131. {
  1132. // Fonts previously in getFontListSans() have moved to fonts.xml.
  1133. return std::vector<std::string>();
  1134. }
  1135. void LLWindowMacOSX::updateMouseDeltas(float* deltas)
  1136. {
  1137. if (mCursorDecoupled && deltas)
  1138. {
  1139. if (mCursorIgnoreNextDelta)
  1140. {
  1141. mCursorLastEventDeltaX = mCursorLastEventDeltaY = 0;
  1142. mCursorIgnoreNextDelta = false;
  1143. }
  1144. else
  1145. {
  1146. mCursorLastEventDeltaX = ll_round(deltas[0]);
  1147. mCursorLastEventDeltaY = ll_round(-deltas[1]);
  1148. }
  1149. }
  1150. else
  1151. {
  1152. mCursorLastEventDeltaX = mCursorLastEventDeltaY = 0;
  1153. }
  1154. }
  1155. void LLWindowMacOSX::getMouseDeltas(float* deltas)
  1156. {
  1157. if (deltas)
  1158. {
  1159. deltas[0] = mCursorLastEventDeltaX;
  1160. deltas[1] = mCursorLastEventDeltaY;
  1161. }
  1162. }
  1163. ///////////////////////////////////////////////////////////////////////////////
  1164. // Shared OpenGL context support
  1165. ///////////////////////////////////////////////////////////////////////////////
  1166. struct LLSharedOpenGLContext
  1167. {
  1168. CGLContextObj mContext;
  1169. };
  1170. void* LLWindowMacOSX::createSharedContext()
  1171. {
  1172. LLSharedOpenGLContext* context = new LLSharedOpenGLContext;
  1173. CGLCreateContext(mPixelFormat, mContext, &(context->mContext));
  1174. if (!context->mContext)
  1175. {
  1176. // Something went (very) wrong... Free the structure and return a NULL
  1177. // pointer to signify we do not have a GL context available. HB
  1178. llwarns_sparse << "Failed to create a new shared GL context."
  1179. << llendl;
  1180. delete context;
  1181. return NULL;
  1182. }
  1183. if (sUseMultGL)
  1184. {
  1185. CGLEnable(mContext, kCGLCEMPEngine);
  1186. }
  1187. return (void*)context;
  1188. }
  1189. void LLWindowMacOSX::makeContextCurrent(void* context)
  1190. {
  1191. if (context)
  1192. {
  1193. CGLSetCurrentContext(((LLSharedOpenGLContext*)context)->mContext);
  1194. }
  1195. else
  1196. {
  1197. // Restore main GL thread context.
  1198. CGLSetCurrentContext(mContext);
  1199. }
  1200. }
  1201. void LLWindowMacOSX::destroySharedContext(void* context)
  1202. {
  1203. if (context) // Ignore attempts to destroy invalid contexts. HB
  1204. {
  1205. LLSharedOpenGLContext* sc = (LLSharedOpenGLContext*)context;
  1206. CGLDestroyContext(sc->mContext);
  1207. delete sc;
  1208. }
  1209. }
  1210. ///////////////////////////////////////////////////////////////////////////////
  1211. // These functions are used as wrappers for our internal event handling
  1212. // callbacks. It is a good idea to wrap these to avoid reworking more code than
  1213. // we need to within LLWindow.
  1214. ///////////////////////////////////////////////////////////////////////////////
  1215. bool callKeyUp(NSKeyEventRef event, unsigned short key, unsigned int mask)
  1216. {
  1217. bool ret_val = false;
  1218. if (gKeyboardp)
  1219. {
  1220. mRawKeyEvent = event;
  1221. ret_val = gKeyboardp->handleKeyUp(key, mask);
  1222. mRawKeyEvent = NULL;
  1223. }
  1224. return ret_val;
  1225. }
  1226. bool callKeyDown(NSKeyEventRef event, unsigned short key, unsigned int mask)
  1227. {
  1228. bool ret_val = false;
  1229. if (gKeyboardp)
  1230. {
  1231. mRawKeyEvent = event;
  1232. ret_val = gKeyboardp->handleKeyDown(key, mask);
  1233. mRawKeyEvent = NULL;
  1234. }
  1235. return ret_val;
  1236. }
  1237. void callResetKeys()
  1238. {
  1239. if (gKeyboardp)
  1240. {
  1241. gKeyboardp->resetKeys();
  1242. }
  1243. }
  1244. bool callUnicodeCallback(wchar_t character, unsigned int mask)
  1245. {
  1246. if (!sWindowImplementation) return false;
  1247. LLWindowCallbacks* callbacks = sWindowImplementation->getCallbacks();
  1248. if (!callbacks) return false;
  1249. NativeKeyEventData event_data;
  1250. memset(&event_data, 0, sizeof(NativeKeyEventData));
  1251. event_data.mKeyEvent = NativeKeyEventData::KEYCHAR;
  1252. event_data.mEventType = 0;
  1253. event_data.mEventModifiers = mask;
  1254. event_data.mEventKeyCode = 0;
  1255. event_data.mEventChars = character;
  1256. event_data.mEventUnmodChars = character;
  1257. event_data.mEventRepeat = false;
  1258. mRawKeyEvent = &event_data;
  1259. bool result = callbacks->handleUnicodeChar(character, mask);
  1260. mRawKeyEvent = NULL;
  1261. return result;
  1262. }
  1263. void callFocus()
  1264. {
  1265. if (sWindowImplementation)
  1266. {
  1267. LLWindowCallbacks* callbacks = sWindowImplementation->getCallbacks();
  1268. if (callbacks)
  1269. {
  1270. callbacks->handleFocus(sWindowImplementation);
  1271. }
  1272. }
  1273. }
  1274. void callFocusLost()
  1275. {
  1276. if (sWindowImplementation)
  1277. {
  1278. LLWindowCallbacks* callbacks = sWindowImplementation->getCallbacks();
  1279. if (callbacks)
  1280. {
  1281. callbacks->handleFocusLost(sWindowImplementation);
  1282. }
  1283. }
  1284. }
  1285. void callRightMouseDown(float* pos, MASK mask)
  1286. {
  1287. if (!sWindowImplementation || !gKeyboardp) return;
  1288. LLWindowCallbacks* callbacks = sWindowImplementation->getCallbacks();
  1289. if (!callbacks) return;
  1290. if (sWindowImplementation->allowsLanguageInput())
  1291. {
  1292. sWindowImplementation->interruptLanguageTextInput();
  1293. }
  1294. LLCoordGL out_coords;
  1295. out_coords.mX = ll_round(pos[0]);
  1296. out_coords.mY = ll_round(pos[1]);
  1297. callbacks->handleRightMouseDown(sWindowImplementation, out_coords,
  1298. gKeyboardp->currentMask(true));
  1299. }
  1300. void callRightMouseUp(float* pos, MASK mask)
  1301. {
  1302. if (!sWindowImplementation || !gKeyboardp) return;
  1303. LLWindowCallbacks* callbacks = sWindowImplementation->getCallbacks();
  1304. if (!callbacks) return;
  1305. if (sWindowImplementation->allowsLanguageInput())
  1306. {
  1307. sWindowImplementation->interruptLanguageTextInput();
  1308. }
  1309. LLCoordGL out_coords;
  1310. out_coords.mX = ll_round(pos[0]);
  1311. out_coords.mY = ll_round(pos[1]);
  1312. callbacks->handleRightMouseUp(sWindowImplementation, out_coords,
  1313. gKeyboardp->currentMask(true));
  1314. }
  1315. void callLeftMouseDown(float* pos, MASK mask)
  1316. {
  1317. if (!sWindowImplementation || !gKeyboardp) return;
  1318. LLWindowCallbacks* callbacks = sWindowImplementation->getCallbacks();
  1319. if (!callbacks) return;
  1320. if (sWindowImplementation->allowsLanguageInput())
  1321. {
  1322. sWindowImplementation->interruptLanguageTextInput();
  1323. }
  1324. LLCoordGL out_coords;
  1325. out_coords.mX = ll_round(pos[0]);
  1326. out_coords.mY = ll_round(pos[1]);
  1327. callbacks->handleMouseDown(sWindowImplementation, out_coords,
  1328. gKeyboardp->currentMask(true));
  1329. }
  1330. void callLeftMouseUp(float* pos, MASK mask)
  1331. {
  1332. if (!sWindowImplementation || !gKeyboardp) return;
  1333. LLWindowCallbacks* callbacks = sWindowImplementation->getCallbacks();
  1334. if (!callbacks) return;
  1335. if (sWindowImplementation->allowsLanguageInput())
  1336. {
  1337. sWindowImplementation->interruptLanguageTextInput();
  1338. }
  1339. LLCoordGL out_coords;
  1340. out_coords.mX = ll_round(pos[0]);
  1341. out_coords.mY = ll_round(pos[1]);
  1342. callbacks->handleMouseUp(sWindowImplementation, out_coords,
  1343. gKeyboardp->currentMask(true));
  1344. }
  1345. void callDoubleClick(float* pos, MASK mask)
  1346. {
  1347. if (!sWindowImplementation || !gKeyboardp) return;
  1348. LLWindowCallbacks* callbacks = sWindowImplementation->getCallbacks();
  1349. if (!callbacks) return;
  1350. if (sWindowImplementation->allowsLanguageInput())
  1351. {
  1352. sWindowImplementation->interruptLanguageTextInput();
  1353. }
  1354. LLCoordGL out_coords;
  1355. out_coords.mX = ll_round(pos[0]);
  1356. out_coords.mY = ll_round(pos[1]);
  1357. callbacks->handleDoubleClick(sWindowImplementation, out_coords,
  1358. gKeyboardp->currentMask(true));
  1359. }
  1360. void callResize(unsigned int width, unsigned int height)
  1361. {
  1362. if (sWindowImplementation)
  1363. {
  1364. LLWindowCallbacks* callbacks = sWindowImplementation->getCallbacks();
  1365. if (callbacks)
  1366. {
  1367. callbacks->handleResize(sWindowImplementation, width, height);
  1368. }
  1369. }
  1370. }
  1371. void callMouseMoved(float* pos, MASK mask)
  1372. {
  1373. if (!sWindowImplementation || !gKeyboardp) return;
  1374. LLWindowCallbacks* callbacks = sWindowImplementation->getCallbacks();
  1375. if (!callbacks) return;
  1376. LLCoordGL out_coords;
  1377. out_coords.mX = ll_round(pos[0]);
  1378. out_coords.mY = ll_round(pos[1]);
  1379. float deltas[2];
  1380. sWindowImplementation->getMouseDeltas(deltas);
  1381. out_coords.mX += deltas[0];
  1382. out_coords.mY += deltas[1];
  1383. callbacks->handleMouseMove(sWindowImplementation, out_coords,
  1384. gKeyboardp->currentMask(true));
  1385. }
  1386. void callMouseDragged(float* pos, MASK mask)
  1387. {
  1388. if (!sWindowImplementation || !gKeyboardp) return;
  1389. LLWindowCallbacks* callbacks = sWindowImplementation->getCallbacks();
  1390. if (!callbacks) return;
  1391. LLCoordGL out_coords;
  1392. out_coords.mX = ll_round(pos[0]);
  1393. out_coords.mY = ll_round(pos[1]);
  1394. float deltas[2];
  1395. sWindowImplementation->getMouseDeltas(deltas);
  1396. out_coords.mX += deltas[0];
  1397. out_coords.mY += deltas[1];
  1398. callbacks->handleMouseDragged(sWindowImplementation, out_coords,
  1399. gKeyboardp->currentMask(true));
  1400. }
  1401. void callScrollMoved(float delta)
  1402. {
  1403. if (sWindowImplementation)
  1404. {
  1405. LLWindowCallbacks* callbacks = sWindowImplementation->getCallbacks();
  1406. if (callbacks)
  1407. {
  1408. callbacks->handleScrollWheel(sWindowImplementation, delta);
  1409. }
  1410. }
  1411. }
  1412. void callMouseExit()
  1413. {
  1414. if (sWindowImplementation)
  1415. {
  1416. LLWindowCallbacks* callbacks = sWindowImplementation->getCallbacks();
  1417. if (callbacks)
  1418. {
  1419. callbacks->handleMouseLeave(sWindowImplementation);
  1420. }
  1421. }
  1422. }
  1423. void callWindowFocus()
  1424. {
  1425. if (sWindowImplementation)
  1426. {
  1427. LLWindowCallbacks* callbacks = sWindowImplementation->getCallbacks();
  1428. if (callbacks)
  1429. {
  1430. callbacks->handleFocus(sWindowImplementation);
  1431. return;
  1432. }
  1433. }
  1434. llwarns << "Window implementation or callbacks not yet initialized."
  1435. << llendl;
  1436. }
  1437. void callWindowUnfocus()
  1438. {
  1439. if (sWindowImplementation)
  1440. {
  1441. LLWindowCallbacks* callbacks = sWindowImplementation->getCallbacks();
  1442. if (callbacks)
  1443. {
  1444. callbacks->handleFocusLost(sWindowImplementation);
  1445. }
  1446. }
  1447. }
  1448. void callWindowHide()
  1449. {
  1450. if (sWindowImplementation)
  1451. {
  1452. LLWindowCallbacks* callbacks = sWindowImplementation->getCallbacks();
  1453. if (callbacks)
  1454. {
  1455. callbacks->handleActivate(sWindowImplementation, false);
  1456. }
  1457. }
  1458. }
  1459. void callWindowUnhide()
  1460. {
  1461. if (sWindowImplementation)
  1462. {
  1463. LLWindowCallbacks* callbacks = sWindowImplementation->getCallbacks();
  1464. if (callbacks)
  1465. {
  1466. callbacks->handleActivate(sWindowImplementation, true);
  1467. }
  1468. }
  1469. }
  1470. void callWindowDidChangeScreen()
  1471. {
  1472. if (sWindowImplementation)
  1473. {
  1474. LLWindowCallbacks* callbacks = sWindowImplementation->getCallbacks();
  1475. if (callbacks)
  1476. {
  1477. callbacks->handleWindowDidChangeScreen(sWindowImplementation);
  1478. }
  1479. }
  1480. }
  1481. void callDeltaUpdate(float* delta, MASK mask)
  1482. {
  1483. if (sWindowImplementation)
  1484. {
  1485. sWindowImplementation->updateMouseDeltas(delta);
  1486. }
  1487. }
  1488. void callMiddleMouseDown(float* pos, MASK mask)
  1489. {
  1490. if (!sWindowImplementation) return;
  1491. LLWindowCallbacks* callbacks = sWindowImplementation->getCallbacks();
  1492. if (!callbacks) return;
  1493. LLCoordGL out_coords;
  1494. out_coords.mX = ll_round(pos[0]);
  1495. out_coords.mY = ll_round(pos[1]);
  1496. float deltas[2];
  1497. sWindowImplementation->getMouseDeltas(deltas);
  1498. out_coords.mX += deltas[0];
  1499. out_coords.mY += deltas[1];
  1500. callbacks->handleMiddleMouseDown(sWindowImplementation, out_coords, mask);
  1501. }
  1502. void callMiddleMouseUp(float* pos, MASK mask)
  1503. {
  1504. if (!sWindowImplementation) return;
  1505. LLWindowCallbacks* callbacks = sWindowImplementation->getCallbacks();
  1506. if (!callbacks) return;
  1507. LLCoordGL out_coords;
  1508. out_coords.mX = ll_round(pos[0]);
  1509. out_coords.mY = ll_round(pos[1]);
  1510. float deltas[2];
  1511. sWindowImplementation->getMouseDeltas(deltas);
  1512. out_coords.mX += deltas[0];
  1513. out_coords.mY += deltas[1];
  1514. callbacks->handleMiddleMouseUp(sWindowImplementation, out_coords, mask);
  1515. }
  1516. void callQuitHandler()
  1517. {
  1518. if (sWindowImplementation)
  1519. {
  1520. LLWindowCallbacks* callbacks = sWindowImplementation->getCallbacks();
  1521. if (callbacks)
  1522. {
  1523. callbacks->handleQuit(sWindowImplementation);
  1524. }
  1525. }
  1526. }
  1527. void getPreeditSelectionRange(int* position, int* length)
  1528. {
  1529. if (sWindowImplementation)
  1530. {
  1531. LLPreeditor* preeditor = sWindowImplementation->getPreeditor();
  1532. if (preeditor)
  1533. {
  1534. preeditor->getSelectionRange(position, length);
  1535. }
  1536. }
  1537. }
  1538. void getPreeditMarkedRange(int* position, int* length)
  1539. {
  1540. if (sWindowImplementation)
  1541. {
  1542. LLPreeditor* preeditor = sWindowImplementation->getPreeditor();
  1543. if (preeditor)
  1544. {
  1545. preeditor->getPreeditRange(position, length);
  1546. }
  1547. }
  1548. }
  1549. void setPreeditMarkedRange(int position, int length)
  1550. {
  1551. if (sWindowImplementation)
  1552. {
  1553. LLPreeditor* preeditor = sWindowImplementation->getPreeditor();
  1554. if (preeditor)
  1555. {
  1556. preeditor->markAsPreedit(position, length);
  1557. }
  1558. }
  1559. }
  1560. bool handleUnicodeCharacter(wchar_t c)
  1561. {
  1562. if (sWindowImplementation)
  1563. {
  1564. LLPreeditor* preeditor = sWindowImplementation->getPreeditor();
  1565. if (preeditor)
  1566. {
  1567. return preeditor->handleUnicodeCharHere(c);
  1568. }
  1569. }
  1570. return false;
  1571. }
  1572. void resetPreedit()
  1573. {
  1574. if (sWindowImplementation)
  1575. {
  1576. LLPreeditor* preeditor = sWindowImplementation->getPreeditor();
  1577. if (preeditor)
  1578. {
  1579. preeditor->resetPreedit();
  1580. }
  1581. }
  1582. }
  1583. // For reasons of convenience, handle IME updates here. This largely mirrors the
  1584. // old implementation.
  1585. void setMarkedText(unsigned short* unitext, unsigned int* sel_range,
  1586. unsigned int* replace_range, long text_len,
  1587. attributedStringInfo segments)
  1588. {
  1589. if (!sWindowImplementation) return;
  1590. LLPreeditor* preeditor = sWindowImplementation->getPreeditor();
  1591. if (!preeditor) return;
  1592. preeditor->resetPreedit();
  1593. // This should be a viable replacement for the
  1594. // kEventParamTextInputSendReplaceRange parameter.
  1595. if (replace_range[0] < replace_range[1])
  1596. {
  1597. const LLWString& text = preeditor->getWText();
  1598. S32 location = wstring_length_from_utf16_length(text, 0,
  1599. replace_range[0]);
  1600. S32 length = wstring_length_from_utf16_length(text, location,
  1601. replace_range[1]);
  1602. preeditor->markAsPreedit(location, length);
  1603. }
  1604. LLWString fix_str = utf16str_to_wstring(llutf16string(unitext, text_len));
  1605. S32 caret_position = fix_str.length();
  1606. preeditor->updatePreedit(fix_str, segments.seg_lengths,
  1607. segments.seg_standouts, caret_position);
  1608. }
  1609. void getPreeditLocation(float* location, unsigned int length)
  1610. {
  1611. if (!sWindowImplementation)
  1612. {
  1613. return;
  1614. }
  1615. LLPreeditor* preeditor = sWindowImplementation->getPreeditor();
  1616. if (preeditor)
  1617. {
  1618. LLCoordGL coord;
  1619. LLCoordScreen screen;
  1620. LLRect rect;
  1621. preeditor->getPreeditLocation(length, &coord, &rect, NULL);
  1622. float c[4] = { (float)coord.mX, (float)coord.mY, 0.f, 0.f };
  1623. convertRectToScreen(sWindowImplementation->getPlatformWindow(), c);
  1624. location[0] = c[0];
  1625. location[1] = c[1];
  1626. }
  1627. }
  1628. void callModifier(MASK mask)
  1629. {
  1630. if (gKeyboardp)
  1631. {
  1632. gKeyboardp->handleModifier(mask);
  1633. }
  1634. }
  1635. // Drag and drop into viewer window not yet implemented in the Cool VL Viewer
  1636. void callHandleDragEntered(std::string url)
  1637. {
  1638. }
  1639. void callHandleDragExited(std::string url)
  1640. {
  1641. }
  1642. void callHandleDragUpdated(std::string url)
  1643. {
  1644. }
  1645. void callHandleDragDropped(std::string url)
  1646. {
  1647. }