BsLinuxRenderWindow.cpp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606
  1. //********************************** Banshee Engine (www.banshee3d.com) **************************************************//
  2. //**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
  3. #include "CoreThread/BsCoreThread.h"
  4. #include "Linux/BsLinuxPlatform.h"
  5. #include "Linux/BsLinuxRenderWindow.h"
  6. #include "Linux/BsLinuxWindow.h"
  7. #include "Linux/BsLinuxVideoModeInfo.h"
  8. #include "Linux/BsLinuxGLSupport.h"
  9. #include "Linux/BsLinuxContext.h"
  10. #include "BsGLPixelFormat.h"
  11. #include "BsGLRenderWindowManager.h"
  12. #include "Math/BsMath.h"
  13. #define XRANDR_ROTATION_LEFT (1 << 1)
  14. #define XRANDR_ROTATION_RIGHT (1 << 3)
  15. namespace bs
  16. {
  17. LinuxRenderWindow::LinuxRenderWindow(const RENDER_WINDOW_DESC& desc, UINT32 windowId, ct::LinuxGLSupport& glSupport)
  18. :RenderWindow(desc, windowId), mGLSupport(glSupport), mProperties(desc)
  19. { }
  20. void LinuxRenderWindow::getCustomAttribute(const String& name, void* data) const
  21. {
  22. if (name == "WINDOW" || name == "LINUX_WINDOW")
  23. {
  24. blockUntilCoreInitialized();
  25. getCore()->getCustomAttribute(name, data);
  26. return;
  27. }
  28. }
  29. Vector2I LinuxRenderWindow::screenToWindowPos(const Vector2I& screenPos) const
  30. {
  31. blockUntilCoreInitialized();
  32. LinuxPlatform::lockX();
  33. Vector2I pos = getCore()->_getInternal()->screenToWindowPos(screenPos);
  34. LinuxPlatform::unlockX();
  35. return pos;
  36. }
  37. Vector2I LinuxRenderWindow::windowToScreenPos(const Vector2I& windowPos) const
  38. {
  39. blockUntilCoreInitialized();
  40. LinuxPlatform::lockX();
  41. Vector2I pos = getCore()->_getInternal()->windowToScreenPos(windowPos);
  42. LinuxPlatform::unlockX();
  43. return pos;
  44. }
  45. SPtr<ct::LinuxRenderWindow> LinuxRenderWindow::getCore() const
  46. {
  47. return std::static_pointer_cast<ct::LinuxRenderWindow>(mCoreSpecific);
  48. }
  49. void LinuxRenderWindow::syncProperties()
  50. {
  51. ScopedSpinLock lock(getCore()->_getPropertiesLock());
  52. mProperties = getCore()->mSyncedProperties;
  53. }
  54. namespace ct
  55. {
  56. LinuxRenderWindow::LinuxRenderWindow(const RENDER_WINDOW_DESC& desc, UINT32 windowId, LinuxGLSupport& glsupport)
  57. : RenderWindow(desc, windowId), mWindow(nullptr), mGLSupport(glsupport), mContext(nullptr), mProperties(desc)
  58. , mSyncedProperties(desc), mIsChild(false), mShowOnSwap(false)
  59. { }
  60. LinuxRenderWindow::~LinuxRenderWindow()
  61. {
  62. if (mWindow != nullptr)
  63. {
  64. LinuxPlatform::lockX();
  65. mWindow->close();
  66. bs_delete(mWindow);
  67. mWindow = nullptr;
  68. LinuxPlatform::unlockX();
  69. }
  70. }
  71. void LinuxRenderWindow::initialize()
  72. {
  73. LinuxPlatform::lockX();
  74. RenderWindowProperties& props = mProperties;
  75. props.isFullScreen = mDesc.fullscreen;
  76. mIsChild = false;
  77. GLVisualConfig visualConfig = mGLSupport.findBestVisual(LinuxPlatform::getXDisplay(), mDesc.depthBuffer,
  78. mDesc.multisampleCount, mDesc.gamma);
  79. WINDOW_DESC windowDesc;
  80. windowDesc.x = mDesc.left;
  81. windowDesc.y = mDesc.top;
  82. windowDesc.width = mDesc.videoMode.getWidth();
  83. windowDesc.height = mDesc.videoMode.getHeight();
  84. windowDesc.title = mDesc.title;
  85. windowDesc.showDecorations = !mDesc.toolWindow;
  86. windowDesc.allowResize = true;
  87. windowDesc.modal = mDesc.modal;
  88. windowDesc.visualInfo = visualConfig.visualInfo;
  89. windowDesc.screen = mDesc.videoMode.getOutputIdx();
  90. NameValuePairList::const_iterator opt;
  91. opt = mDesc.platformSpecific.find("parentWindowHandle");
  92. if (opt != mDesc.platformSpecific.end())
  93. windowDesc.parent = (::Window)parseUINT64(opt->second);
  94. else
  95. windowDesc.parent = 0;
  96. mIsChild = windowDesc.parent != 0;
  97. props.isFullScreen = mDesc.fullscreen && !mIsChild;
  98. mShowOnSwap = mDesc.hideUntilSwap;
  99. props.isHidden = mDesc.hideUntilSwap || mDesc.hidden;
  100. mWindow = bs_new<LinuxWindow>(windowDesc);
  101. mWindow->_setUserData(this);
  102. props.width = mWindow->getWidth();
  103. props.height = mWindow->getHeight();
  104. props.top = mWindow->getTop();
  105. props.left = mWindow->getLeft();
  106. props.hwGamma = visualConfig.caps.srgb;
  107. props.multisampleCount = visualConfig.caps.numSamples;
  108. XWindowAttributes windowAttributes;
  109. XGetWindowAttributes(LinuxPlatform::getXDisplay(), mWindow->_getXWindow(), &windowAttributes);
  110. XVisualInfo requestVI;
  111. requestVI.screen = windowDesc.screen;
  112. requestVI.visualid = XVisualIDFromVisual(windowAttributes.visual);
  113. LinuxPlatform::unlockX(); // Calls below have their own locking mechanisms
  114. mContext = mGLSupport.createContext(LinuxPlatform::getXDisplay(), requestVI);
  115. if(mDesc.fullscreen && !mIsChild)
  116. setFullscreen(mDesc.videoMode);
  117. if(mDesc.hideUntilSwap || mDesc.hidden)
  118. setHidden(true);
  119. {
  120. ScopedSpinLock lock(mLock);
  121. mSyncedProperties = props;
  122. }
  123. bs::RenderWindowManager::instance().notifySyncDataDirty(this);
  124. RenderWindow::initialize();
  125. }
  126. void LinuxRenderWindow::setFullscreen(UINT32 width, UINT32 height, float refreshRate, UINT32 monitorIdx)
  127. {
  128. THROW_IF_NOT_CORE_THREAD;
  129. VideoMode videoMode(width, height, refreshRate, monitorIdx);
  130. setFullscreen(videoMode);
  131. }
  132. void LinuxRenderWindow::setVideoMode(INT32 screen, RROutput output, RRMode mode)
  133. {
  134. ::Display* display = LinuxPlatform::getXDisplay();
  135. ::Window rootWindow = RootWindow(display, screen);
  136. XRRScreenResources* screenRes = XRRGetScreenResources (display, rootWindow);
  137. if(screenRes == nullptr)
  138. {
  139. LOGERR("XRR: Failed to retrieve screen resources. ");
  140. return;
  141. }
  142. XRROutputInfo* outputInfo = XRRGetOutputInfo(display, screenRes, output);
  143. if(outputInfo == nullptr)
  144. {
  145. XRRFreeScreenResources(screenRes);
  146. LOGERR("XRR: Failed to retrieve output info for output: " + toString((UINT32)output));
  147. return;
  148. }
  149. XRRCrtcInfo* crtcInfo = XRRGetCrtcInfo(display, screenRes, outputInfo->crtc);
  150. if(crtcInfo == nullptr)
  151. {
  152. XRRFreeScreenResources(screenRes);
  153. XRRFreeOutputInfo(outputInfo);
  154. LOGERR("XRR: Failed to retrieve CRTC info for output: " + toString((UINT32)output));
  155. return;
  156. }
  157. // Note: This changes the user's desktop resolution permanently, even when the app exists, make sure to revert
  158. // (Sadly there doesn't appear to be a better way)
  159. Status status = XRRSetCrtcConfig (display, screenRes, outputInfo->crtc, CurrentTime,
  160. crtcInfo->x, crtcInfo->y, mode, crtcInfo->rotation, &output, 1);
  161. if(status != Success)
  162. LOGERR("XRR: XRRSetCrtcConfig failed.");
  163. XRRFreeCrtcInfo(crtcInfo);
  164. XRRFreeOutputInfo(outputInfo);
  165. XRRFreeScreenResources(screenRes);
  166. }
  167. void LinuxRenderWindow::setFullscreen(const VideoMode& mode)
  168. {
  169. THROW_IF_NOT_CORE_THREAD;
  170. if (mIsChild)
  171. return;
  172. const LinuxVideoModeInfo& videoModeInfo =
  173. static_cast<const LinuxVideoModeInfo&>(RenderAPI::instance().getVideoModeInfo());
  174. UINT32 outputIdx = mode.getOutputIdx();
  175. if(outputIdx >= videoModeInfo.getNumOutputs())
  176. {
  177. LOGERR("Invalid output device index.")
  178. return;
  179. }
  180. const LinuxVideoOutputInfo& outputInfo =
  181. static_cast<const LinuxVideoOutputInfo&>(videoModeInfo.getOutputInfo (outputIdx));
  182. INT32 screen = outputInfo._getScreen();
  183. RROutput outputID = outputInfo._getOutputID();
  184. RRMode modeID = 0;
  185. if(!mode.isCustom())
  186. {
  187. const LinuxVideoMode& videoMode = static_cast<const LinuxVideoMode&>(mode);
  188. modeID = videoMode._getModeID();
  189. }
  190. else
  191. {
  192. LinuxPlatform::lockX();
  193. // Look for mode matching the requested resolution
  194. ::Display* display = LinuxPlatform::getXDisplay();
  195. ::Window rootWindow = RootWindow(display, screen);
  196. XRRScreenResources* screenRes = XRRGetScreenResources(display, rootWindow);
  197. if (screenRes == nullptr)
  198. {
  199. LOGERR("XRR: Failed to retrieve screen resources. ");
  200. return;
  201. }
  202. XRROutputInfo* outputInfo = XRRGetOutputInfo(display, screenRes, outputID);
  203. if (outputInfo == nullptr)
  204. {
  205. XRRFreeScreenResources(screenRes);
  206. LOGERR("XRR: Failed to retrieve output info for output: " + toString((UINT32)outputID));
  207. return;
  208. }
  209. XRRCrtcInfo* crtcInfo = XRRGetCrtcInfo(display, screenRes, outputInfo->crtc);
  210. if (crtcInfo == nullptr)
  211. {
  212. XRRFreeScreenResources(screenRes);
  213. XRRFreeOutputInfo(outputInfo);
  214. LOGERR("XRR: Failed to retrieve CRTC info for output: " + toString((UINT32)outputID));
  215. return;
  216. }
  217. bool foundMode = false;
  218. for (INT32 i = 0; i < screenRes->nmode; i++)
  219. {
  220. const XRRModeInfo& modeInfo = screenRes->modes[i];
  221. UINT32 width, height;
  222. if (crtcInfo->rotation & (XRANDR_ROTATION_LEFT | XRANDR_ROTATION_RIGHT))
  223. {
  224. width = modeInfo.height;
  225. height = modeInfo.width;
  226. }
  227. else
  228. {
  229. width = modeInfo.width;
  230. height = modeInfo.height;
  231. }
  232. float refreshRate;
  233. if (modeInfo.hTotal != 0 && modeInfo.vTotal != 0)
  234. refreshRate = (float) (modeInfo.dotClock / (double) (modeInfo.hTotal * modeInfo.vTotal));
  235. else
  236. refreshRate = 0.0f;
  237. if (width == mode.getWidth() && height == mode.getHeight())
  238. {
  239. modeID = modeInfo.id;
  240. foundMode = true;
  241. if (Math::approxEquals(refreshRate, mode.getRefreshRate()))
  242. break;
  243. }
  244. }
  245. if (!foundMode)
  246. {
  247. LinuxPlatform::unlockX();
  248. LOGERR("Unable to enter fullscreen, unsupported video mode requested.");
  249. return;
  250. }
  251. LinuxPlatform::unlockX();
  252. }
  253. LinuxPlatform::lockX();
  254. setVideoMode(screen, outputID, modeID);
  255. mWindow->_setFullscreen(true);
  256. LinuxPlatform::unlockX();
  257. RenderWindowProperties& props = mProperties;
  258. props.isFullScreen = true;
  259. props.top = 0;
  260. props.left = 0;
  261. props.width = mode.getWidth();
  262. props.height = mode.getHeight();
  263. _windowMovedOrResized();
  264. }
  265. void LinuxRenderWindow::setWindowed(UINT32 width, UINT32 height)
  266. {
  267. THROW_IF_NOT_CORE_THREAD;
  268. RenderWindowProperties& props = mProperties;
  269. if (!props.isFullScreen)
  270. return;
  271. // Restore old screen config
  272. const LinuxVideoModeInfo& videoModeInfo =
  273. static_cast<const LinuxVideoModeInfo&>(RenderAPI::instance().getVideoModeInfo());
  274. UINT32 outputIdx = 0; // 0 is always primary
  275. if(outputIdx >= videoModeInfo.getNumOutputs())
  276. {
  277. LOGERR("Invalid output device index.")
  278. return;
  279. }
  280. const LinuxVideoOutputInfo& outputInfo =
  281. static_cast<const LinuxVideoOutputInfo&>(videoModeInfo.getOutputInfo (outputIdx));
  282. const LinuxVideoMode& desktopVideoMode = static_cast<const LinuxVideoMode&>(outputInfo.getDesktopVideoMode());
  283. LinuxPlatform::lockX();
  284. setVideoMode(outputInfo._getScreen(), outputInfo._getOutputID(), desktopVideoMode._getModeID());
  285. mWindow->_setFullscreen(false);
  286. LinuxPlatform::unlockX();
  287. props.isFullScreen = false;
  288. props.width = width;
  289. props.height = height;
  290. {
  291. ScopedSpinLock lock(mLock);
  292. mSyncedProperties.width = props.width;
  293. mSyncedProperties.height = props.height;
  294. }
  295. bs::RenderWindowManager::instance().notifySyncDataDirty(this);
  296. _windowMovedOrResized();
  297. }
  298. void LinuxRenderWindow::move(INT32 left, INT32 top)
  299. {
  300. THROW_IF_NOT_CORE_THREAD;
  301. RenderWindowProperties& props = mProperties;
  302. if (!props.isFullScreen)
  303. {
  304. LinuxPlatform::lockX();
  305. mWindow->move(left, top);
  306. LinuxPlatform::unlockX();
  307. props.top = mWindow->getTop();
  308. props.left = mWindow->getLeft();
  309. {
  310. ScopedSpinLock lock(mLock);
  311. mSyncedProperties.top = props.top;
  312. mSyncedProperties.left = props.left;
  313. }
  314. bs::RenderWindowManager::instance().notifySyncDataDirty(this);
  315. }
  316. }
  317. void LinuxRenderWindow::resize(UINT32 width, UINT32 height)
  318. {
  319. THROW_IF_NOT_CORE_THREAD;
  320. RenderWindowProperties& props = mProperties;
  321. if (!props.isFullScreen)
  322. {
  323. LinuxPlatform::lockX();
  324. mWindow->resize(width, height);
  325. LinuxPlatform::unlockX();
  326. props.width = mWindow->getWidth();
  327. props.height = mWindow->getHeight();
  328. {
  329. ScopedSpinLock lock(mLock);
  330. mSyncedProperties.width = props.width;
  331. mSyncedProperties.height = props.height;
  332. }
  333. bs::RenderWindowManager::instance().notifySyncDataDirty(this);
  334. }
  335. }
  336. void LinuxRenderWindow::minimize()
  337. {
  338. THROW_IF_NOT_CORE_THREAD;
  339. LinuxPlatform::lockX();
  340. mWindow->minimize();
  341. LinuxPlatform::unlockX();
  342. }
  343. void LinuxRenderWindow::maximize()
  344. {
  345. THROW_IF_NOT_CORE_THREAD;
  346. LinuxPlatform::lockX();
  347. mWindow->maximize();
  348. LinuxPlatform::unlockX();
  349. }
  350. void LinuxRenderWindow::restore()
  351. {
  352. THROW_IF_NOT_CORE_THREAD;
  353. LinuxPlatform::lockX();
  354. mWindow->restore();
  355. LinuxPlatform::unlockX();
  356. }
  357. void LinuxRenderWindow::swapBuffers(UINT32 syncMask)
  358. {
  359. THROW_IF_NOT_CORE_THREAD;
  360. if (mShowOnSwap)
  361. setHidden(false);
  362. LinuxPlatform::lockX();
  363. glXSwapBuffers(LinuxPlatform::getXDisplay(), mWindow->_getXWindow());
  364. LinuxPlatform::unlockX();
  365. }
  366. void LinuxRenderWindow::copyToMemory(PixelData &dst, FrameBuffer buffer)
  367. {
  368. THROW_IF_NOT_CORE_THREAD;
  369. if ((dst.getRight() > getProperties().width) ||
  370. (dst.getBottom() > getProperties().height) ||
  371. (dst.getFront() != 0) || (dst.getBack() != 1))
  372. {
  373. BS_EXCEPT(InvalidParametersException, "Invalid box.");
  374. }
  375. if (buffer == FB_AUTO)
  376. {
  377. buffer = mProperties.isFullScreen ? FB_FRONT : FB_BACK;
  378. }
  379. GLenum format = GLPixelUtil::getGLOriginFormat(dst.getFormat());
  380. GLenum type = GLPixelUtil::getGLOriginDataType(dst.getFormat());
  381. if ((format == GL_NONE) || (type == 0))
  382. {
  383. BS_EXCEPT(InvalidParametersException, "Unsupported format.");
  384. }
  385. // Must change the packing to ensure no overruns!
  386. glPixelStorei(GL_PACK_ALIGNMENT, 1);
  387. glReadBuffer((buffer == FB_FRONT)? GL_FRONT : GL_BACK);
  388. glReadPixels((GLint)dst.getLeft(), (GLint)dst.getTop(),
  389. (GLsizei)dst.getWidth(), (GLsizei)dst.getHeight(),
  390. format, type, dst.getData());
  391. // restore default alignment
  392. glPixelStorei(GL_PACK_ALIGNMENT, 4);
  393. //vertical flip
  394. {
  395. size_t rowSpan = dst.getWidth() * PixelUtil::getNumElemBytes(dst.getFormat());
  396. size_t height = dst.getHeight();
  397. UINT8* tmpData = (UINT8*)bs_alloc((UINT32)(rowSpan * height));
  398. UINT8* srcRow = (UINT8 *)dst.getData(), *tmpRow = tmpData + (height - 1) * rowSpan;
  399. while (tmpRow >= tmpData)
  400. {
  401. memcpy(tmpRow, srcRow, rowSpan);
  402. srcRow += rowSpan;
  403. tmpRow -= rowSpan;
  404. }
  405. memcpy(dst.getData(), tmpData, rowSpan * height);
  406. bs_free(tmpData);
  407. }
  408. }
  409. void LinuxRenderWindow::getCustomAttribute(const String& name, void* data) const
  410. {
  411. if(name == "GLCONTEXT")
  412. {
  413. SPtr<GLContext>* contextPtr = static_cast<SPtr<GLContext>*>(data);
  414. *contextPtr = mContext;
  415. return;
  416. }
  417. else if(name == "LINUX_WINDOW")
  418. {
  419. LinuxWindow** window = (LinuxWindow**)data;
  420. *window = mWindow;
  421. return;
  422. }
  423. else if(name == "WINDOW")
  424. {
  425. ::Window* window = (::Window*)data;
  426. *window = mWindow->_getXWindow();
  427. return;
  428. }
  429. }
  430. void LinuxRenderWindow::setActive(bool state)
  431. {
  432. THROW_IF_NOT_CORE_THREAD;
  433. LinuxPlatform::lockX();
  434. if(state)
  435. mWindow->restore();
  436. else
  437. mWindow->minimize();
  438. LinuxPlatform::unlockX();
  439. RenderWindow::setActive(state);
  440. }
  441. void LinuxRenderWindow::setHidden(bool hidden)
  442. {
  443. THROW_IF_NOT_CORE_THREAD;
  444. mShowOnSwap = false;
  445. LinuxPlatform::lockX();
  446. if(hidden)
  447. mWindow->hide();
  448. else
  449. mWindow->show();
  450. LinuxPlatform::unlockX();
  451. RenderWindow::setHidden(hidden);
  452. }
  453. void LinuxRenderWindow::_windowMovedOrResized()
  454. {
  455. if (!mWindow)
  456. return;
  457. RenderWindowProperties& props = mProperties;
  458. if (!props.isFullScreen) // Fullscreen is handled directly by this object
  459. {
  460. props.top = mWindow->getTop();
  461. props.left = mWindow->getLeft();
  462. props.width = mWindow->getWidth();
  463. props.height = mWindow->getHeight();
  464. }
  465. RenderWindow::_windowMovedOrResized();
  466. }
  467. void LinuxRenderWindow::syncProperties()
  468. {
  469. ScopedSpinLock lock(mLock);
  470. mProperties = mSyncedProperties;
  471. }
  472. }}