BsLinuxRenderWindow.cpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536
  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. namespace bs
  13. {
  14. LinuxRenderWindow::LinuxRenderWindow(const RENDER_WINDOW_DESC& desc, UINT32 windowId, ct::LinuxGLSupport& glSupport)
  15. :RenderWindow(desc, windowId), mGLSupport(glSupport), mProperties(desc)
  16. { }
  17. void LinuxRenderWindow::getCustomAttribute(const String& name, void* data) const
  18. {
  19. if (name == "WINDOW")
  20. {
  21. blockUntilCoreInitialized();
  22. getCore()->getCustomAttribute(name, data);
  23. return;
  24. }
  25. }
  26. Vector2I LinuxRenderWindow::screenToWindowPos(const Vector2I& screenPos) const
  27. {
  28. blockUntilCoreInitialized();
  29. LinuxPlatform::lockX();
  30. Vector2I pos = getCore()->_getInternal()->screenToWindowPos(screenPos);
  31. LinuxPlatform::unlockX();
  32. return pos;
  33. }
  34. Vector2I LinuxRenderWindow::windowToScreenPos(const Vector2I& windowPos) const
  35. {
  36. blockUntilCoreInitialized();
  37. LinuxPlatform::lockX();
  38. Vector2I pos = getCore()->_getInternal()->windowToScreenPos(windowPos);
  39. LinuxPlatform::unlockX();
  40. return pos;
  41. }
  42. SPtr<ct::LinuxRenderWindow> LinuxRenderWindow::getCore() const
  43. {
  44. return std::static_pointer_cast<ct::LinuxRenderWindow>(mCoreSpecific);
  45. }
  46. void LinuxRenderWindow::syncProperties()
  47. {
  48. ScopedSpinLock lock(getCore()->_getPropertiesLock());
  49. mProperties = getCore()->mSyncedProperties;
  50. }
  51. namespace ct
  52. {
  53. LinuxRenderWindow::LinuxRenderWindow(const RENDER_WINDOW_DESC& desc, UINT32 windowId, LinuxGLSupport& glsupport)
  54. : RenderWindow(desc, windowId), mWindow(nullptr), mGLSupport(glsupport), mContext(nullptr), mProperties(desc)
  55. , mSyncedProperties(desc), mIsChild(false), mShowOnSwap(false), mOldScreenConfig(nullptr)
  56. { }
  57. LinuxRenderWindow::~LinuxRenderWindow()
  58. {
  59. if (mWindow != nullptr)
  60. {
  61. LinuxPlatform::lockX();
  62. if(mOldScreenConfig)
  63. {
  64. XRRFreeScreenConfigInfo(mOldScreenConfig);
  65. mOldScreenConfig = nullptr;
  66. }
  67. mWindow->close();
  68. bs_delete(mWindow);
  69. mWindow = nullptr;
  70. LinuxPlatform::unlockX();
  71. }
  72. }
  73. void LinuxRenderWindow::initialize()
  74. {
  75. LinuxPlatform::lockX();
  76. RenderWindowProperties& props = mProperties;
  77. props.isFullScreen = mDesc.fullscreen;
  78. mIsChild = false;
  79. GLVisualConfig visualConfig = mGLSupport.findBestVisual(LinuxPlatform::getXDisplay(), mDesc.depthBuffer,
  80. mDesc.multisampleCount, mDesc.gamma);
  81. WINDOW_DESC windowDesc;
  82. windowDesc.x = mDesc.left;
  83. windowDesc.y = mDesc.top;
  84. windowDesc.width = mDesc.videoMode.getWidth();
  85. windowDesc.height = mDesc.videoMode.getHeight();
  86. windowDesc.title = mDesc.title;
  87. windowDesc.showDecorations = !mDesc.toolWindow;
  88. windowDesc.modal = mDesc.modal;
  89. windowDesc.visualInfo = visualConfig.visualInfo;
  90. windowDesc.screen = mDesc.videoMode.getOutputIdx();
  91. NameValuePairList::const_iterator opt;
  92. opt = mDesc.platformSpecific.find("parentWindowHandle");
  93. if (opt != mDesc.platformSpecific.end())
  94. windowDesc.parent = (::Window)parseUINT64(opt->second);
  95. mIsChild = windowDesc.parent != 0;
  96. props.isFullScreen = mDesc.fullscreen && !mIsChild;
  97. mShowOnSwap = mDesc.hideUntilSwap;
  98. props.isHidden = mDesc.hideUntilSwap || mDesc.hidden;
  99. mWindow = bs_new<LinuxWindow>(windowDesc);
  100. mWindow->_setUserData(this);
  101. props.width = mWindow->getWidth();
  102. props.height = mWindow->getHeight();
  103. props.top = mWindow->getTop();
  104. props.left = mWindow->getLeft();
  105. props.hwGamma = visualConfig.caps.srgb;
  106. props.multisampleCount = visualConfig.caps.numSamples;
  107. XWindowAttributes windowAttributes;
  108. XGetWindowAttributes(LinuxPlatform::getXDisplay(), mWindow->_getXWindow(), &windowAttributes);
  109. XVisualInfo requestVI;
  110. requestVI.screen = windowDesc.screen;
  111. requestVI.visualid = XVisualIDFromVisual(windowAttributes.visual);
  112. LinuxPlatform::unlockX(); // Calls below have their own locking mechanisms
  113. mContext = mGLSupport.createContext(LinuxPlatform::getXDisplay(), requestVI);
  114. if(mDesc.fullscreen && !mIsChild)
  115. setFullscreen(mDesc.videoMode);
  116. if(mDesc.hideUntilSwap || mDesc.hidden)
  117. setHidden(true);
  118. {
  119. ScopedSpinLock lock(mLock);
  120. mSyncedProperties = props;
  121. }
  122. bs::RenderWindowManager::instance().notifySyncDataDirty(this);
  123. RenderWindow::initialize();
  124. }
  125. void LinuxRenderWindow::setFullscreen(UINT32 width, UINT32 height, float refreshRate, UINT32 monitorIdx)
  126. {
  127. THROW_IF_NOT_CORE_THREAD;
  128. if (mIsChild)
  129. return;
  130. const LinuxVideoModeInfo& videoModeInfo = static_cast<const LinuxVideoModeInfo&>(RenderAPI::instance() .getVideoModeInfo());
  131. UINT32 numOutputs = videoModeInfo.getNumOutputs();
  132. if (numOutputs == 0)
  133. return;
  134. RenderWindowProperties& props = mProperties;
  135. LinuxPlatform::lockX();
  136. ::Display* display = LinuxPlatform::getXDisplay();
  137. // Change video mode if required
  138. bool changeVideoMode = false;
  139. XRRScreenConfiguration* screenConfig = XRRGetScreenInfo(display, XRootWindow(display, DefaultScreen (display)));
  140. Rotation currentRotation;
  141. SizeID currentSizeID = XRRConfigCurrentConfiguration(screenConfig, &currentRotation);
  142. short currentRate = XRRConfigCurrentRate(screenConfig);
  143. int numSizes;
  144. XRRScreenSize* screenSizes = XRRConfigSizes(screenConfig, &numSizes);
  145. if((INT32)width != screenSizes[currentSizeID].width || (INT32)height != screenSizes[currentSizeID].height ||
  146. currentRate != (short)refreshRate)
  147. changeVideoMode = true;
  148. // If provided mode matches current mode, avoid making the video mode change
  149. if(changeVideoMode)
  150. {
  151. // Remember the old config so we can restore it when exiting fullscreen
  152. if(mOldScreenConfig)
  153. {
  154. XRRFreeScreenConfigInfo(mOldScreenConfig);
  155. mOldScreenConfig = nullptr;
  156. }
  157. mOldScreenConfig = screenConfig;
  158. mOldConfigSizeID = currentSizeID;
  159. mOldConfigRate = currentRate;
  160. // Look for size that best matches our requested video mode
  161. bool foundSize = false;
  162. SizeID foundSizeID = 0;
  163. for(int i = 0; i < numSizes; i++)
  164. {
  165. UINT32 curWidth, curHeight;
  166. if(currentRotation == RR_Rotate_90 || currentRotation == RR_Rotate_270)
  167. {
  168. curWidth = (UINT32)screenSizes[i].height;
  169. curHeight = (UINT32)screenSizes[i].width;
  170. }
  171. else
  172. {
  173. curWidth = (UINT32)screenSizes[i].width;
  174. curHeight = (UINT32)screenSizes[i].height;
  175. }
  176. if(curWidth == width && curHeight == height)
  177. {
  178. foundSizeID = (SizeID)i;
  179. foundSize = true;
  180. break;
  181. }
  182. }
  183. if(!foundSize)
  184. LOGERR("Cannot change video mode, requested resolution not supported.");
  185. // Find refresh rate closest to the requested one, or fall back to 60
  186. if(foundSize)
  187. {
  188. int numRates;
  189. short* rates = XRRConfigRates(screenConfig, foundSizeID, &numRates);
  190. short bestRate = 60;
  191. for(int i = 0; i < numRates; i++)
  192. {
  193. if(rates[i] == (short)refreshRate)
  194. {
  195. bestRate = rates[i];
  196. break;
  197. }
  198. else
  199. {
  200. short diffNew = (short)abs((short)refreshRate - rates[i]);
  201. short diffOld = (short)abs((short)refreshRate - bestRate);
  202. if(diffNew < diffOld)
  203. bestRate = rates[i];
  204. }
  205. }
  206. XRRSetScreenConfigAndRate(display, screenConfig, XRootWindow(display, DefaultScreen(display)),
  207. foundSizeID, currentRotation, bestRate, CurrentTime);
  208. }
  209. }
  210. mWindow->_setFullscreen(true);
  211. LinuxPlatform::unlockX();
  212. props.isFullScreen = true;
  213. props.top = 0;
  214. props.left = 0;
  215. props.width = width;
  216. props.height = height;
  217. _windowMovedOrResized();
  218. }
  219. void LinuxRenderWindow::setFullscreen(const VideoMode& mode)
  220. {
  221. THROW_IF_NOT_CORE_THREAD;
  222. setFullscreen(mode.getWidth(), mode.getHeight(), mode.getRefreshRate(), mode.getOutputIdx());
  223. }
  224. void LinuxRenderWindow::setWindowed(UINT32 width, UINT32 height)
  225. {
  226. THROW_IF_NOT_CORE_THREAD;
  227. RenderWindowProperties& props = mProperties;
  228. if (!props.isFullScreen)
  229. return;
  230. props.isFullScreen = false;
  231. props.width = width;
  232. props.height = height;
  233. LinuxPlatform::lockX();
  234. // Restore old screen config
  235. if(mOldScreenConfig)
  236. {
  237. ::Display* display = LinuxPlatform::getXDisplay();
  238. XRRSetScreenConfigAndRate(display, mOldScreenConfig, XRootWindow(display, DefaultScreen(display)),
  239. mOldConfigSizeID, mOldConfigRotation, mOldConfigRate, CurrentTime);
  240. XRRFreeScreenConfigInfo(mOldScreenConfig);
  241. }
  242. mWindow->_setFullscreen(false);
  243. LinuxPlatform::unlockX();
  244. {
  245. ScopedSpinLock lock(mLock);
  246. mSyncedProperties.width = props.width;
  247. mSyncedProperties.height = props.height;
  248. }
  249. bs::RenderWindowManager::instance().notifySyncDataDirty(this);
  250. _windowMovedOrResized();
  251. }
  252. void LinuxRenderWindow::move(INT32 left, INT32 top)
  253. {
  254. THROW_IF_NOT_CORE_THREAD;
  255. RenderWindowProperties& props = mProperties;
  256. if (!props.isFullScreen)
  257. {
  258. LinuxPlatform::lockX();
  259. mWindow->move(left, top);
  260. LinuxPlatform::unlockX();
  261. props.top = mWindow->getTop();
  262. props.left = mWindow->getLeft();
  263. {
  264. ScopedSpinLock lock(mLock);
  265. mSyncedProperties.top = props.top;
  266. mSyncedProperties.left = props.left;
  267. }
  268. bs::RenderWindowManager::instance().notifySyncDataDirty(this);
  269. }
  270. }
  271. void LinuxRenderWindow::resize(UINT32 width, UINT32 height)
  272. {
  273. THROW_IF_NOT_CORE_THREAD;
  274. RenderWindowProperties& props = mProperties;
  275. if (!props.isFullScreen)
  276. {
  277. LinuxPlatform::lockX();
  278. mWindow->resize(width, height);
  279. LinuxPlatform::unlockX();
  280. props.width = mWindow->getWidth();
  281. props.height = mWindow->getHeight();
  282. {
  283. ScopedSpinLock lock(mLock);
  284. mSyncedProperties.width = props.width;
  285. mSyncedProperties.height = props.height;
  286. }
  287. bs::RenderWindowManager::instance().notifySyncDataDirty(this);
  288. }
  289. }
  290. void LinuxRenderWindow::minimize()
  291. {
  292. THROW_IF_NOT_CORE_THREAD;
  293. LinuxPlatform::lockX();
  294. mWindow->minimize();
  295. LinuxPlatform::unlockX();
  296. }
  297. void LinuxRenderWindow::maximize()
  298. {
  299. THROW_IF_NOT_CORE_THREAD;
  300. LinuxPlatform::lockX();
  301. mWindow->maximize();
  302. LinuxPlatform::unlockX();
  303. }
  304. void LinuxRenderWindow::restore()
  305. {
  306. THROW_IF_NOT_CORE_THREAD;
  307. LinuxPlatform::lockX();
  308. mWindow->restore();
  309. LinuxPlatform::unlockX();
  310. }
  311. void LinuxRenderWindow::swapBuffers(UINT32 syncMask)
  312. {
  313. THROW_IF_NOT_CORE_THREAD;
  314. if (mShowOnSwap)
  315. setHidden(false);
  316. LinuxPlatform::lockX();
  317. glXSwapBuffers(LinuxPlatform::getXDisplay(), mWindow->_getXWindow());
  318. LinuxPlatform::unlockX();
  319. }
  320. void LinuxRenderWindow::copyToMemory(PixelData &dst, FrameBuffer buffer)
  321. {
  322. THROW_IF_NOT_CORE_THREAD;
  323. if ((dst.getRight() > getProperties().width) ||
  324. (dst.getBottom() > getProperties().height) ||
  325. (dst.getFront() != 0) || (dst.getBack() != 1))
  326. {
  327. BS_EXCEPT(InvalidParametersException, "Invalid box.");
  328. }
  329. if (buffer == FB_AUTO)
  330. {
  331. buffer = mProperties.isFullScreen ? FB_FRONT : FB_BACK;
  332. }
  333. GLenum format = GLPixelUtil::getGLOriginFormat(dst.getFormat());
  334. GLenum type = GLPixelUtil::getGLOriginDataType(dst.getFormat());
  335. if ((format == GL_NONE) || (type == 0))
  336. {
  337. BS_EXCEPT(InvalidParametersException, "Unsupported format.");
  338. }
  339. // Must change the packing to ensure no overruns!
  340. glPixelStorei(GL_PACK_ALIGNMENT, 1);
  341. glReadBuffer((buffer == FB_FRONT)? GL_FRONT : GL_BACK);
  342. glReadPixels((GLint)dst.getLeft(), (GLint)dst.getTop(),
  343. (GLsizei)dst.getWidth(), (GLsizei)dst.getHeight(),
  344. format, type, dst.getData());
  345. // restore default alignment
  346. glPixelStorei(GL_PACK_ALIGNMENT, 4);
  347. //vertical flip
  348. {
  349. size_t rowSpan = dst.getWidth() * PixelUtil::getNumElemBytes(dst.getFormat());
  350. size_t height = dst.getHeight();
  351. UINT8* tmpData = (UINT8*)bs_alloc((UINT32)(rowSpan * height));
  352. UINT8* srcRow = (UINT8 *)dst.getData(), *tmpRow = tmpData + (height - 1) * rowSpan;
  353. while (tmpRow >= tmpData)
  354. {
  355. memcpy(tmpRow, srcRow, rowSpan);
  356. srcRow += rowSpan;
  357. tmpRow -= rowSpan;
  358. }
  359. memcpy(dst.getData(), tmpData, rowSpan * height);
  360. bs_free(tmpData);
  361. }
  362. }
  363. void LinuxRenderWindow::getCustomAttribute(const String& name, void* data) const
  364. {
  365. if(name == "GLCONTEXT")
  366. {
  367. SPtr<GLContext>* contextPtr = static_cast<SPtr<GLContext>*>(data);
  368. *contextPtr = mContext;
  369. return;
  370. }
  371. else if(name == "WINDOW")
  372. {
  373. LinuxWindow** window = (LinuxWindow**)data;
  374. *window = mWindow;
  375. return;
  376. }
  377. }
  378. void LinuxRenderWindow::setActive(bool state)
  379. {
  380. THROW_IF_NOT_CORE_THREAD;
  381. LinuxPlatform::lockX();
  382. if(state)
  383. mWindow->restore();
  384. else
  385. mWindow->minimize();
  386. LinuxPlatform::unlockX();
  387. RenderWindow::setActive(state);
  388. }
  389. void LinuxRenderWindow::setHidden(bool hidden)
  390. {
  391. THROW_IF_NOT_CORE_THREAD;
  392. mShowOnSwap = false;
  393. LinuxPlatform::lockX();
  394. if(hidden)
  395. mWindow->hide();
  396. else
  397. mWindow->show();
  398. LinuxPlatform::unlockX();
  399. RenderWindow::setHidden(hidden);
  400. }
  401. void LinuxRenderWindow::_windowMovedOrResized()
  402. {
  403. if (!mWindow)
  404. return;
  405. RenderWindowProperties& props = mProperties;
  406. if (!props.isFullScreen) // Fullscreen is handled directly by this object
  407. {
  408. props.top = mWindow->getTop();
  409. props.left = mWindow->getLeft();
  410. props.width = mWindow->getWidth();
  411. props.height = mWindow->getHeight();
  412. }
  413. RenderWindow::_windowMovedOrResized();
  414. }
  415. void LinuxRenderWindow::syncProperties()
  416. {
  417. ScopedSpinLock lock(mLock);
  418. mProperties = mSyncedProperties;
  419. }
  420. }}