x11_monitor.c 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623
  1. //========================================================================
  2. // GLFW 3.4 X11 - www.glfw.org
  3. //------------------------------------------------------------------------
  4. // Copyright (c) 2002-2006 Marcus Geelnard
  5. // Copyright (c) 2006-2019 Camilla Löwy <elmindreda@glfw.org>
  6. //
  7. // This software is provided 'as-is', without any express or implied
  8. // warranty. In no event will the authors be held liable for any damages
  9. // arising from the use of this software.
  10. //
  11. // Permission is granted to anyone to use this software for any purpose,
  12. // including commercial applications, and to alter it and redistribute it
  13. // freely, subject to the following restrictions:
  14. //
  15. // 1. The origin of this software must not be misrepresented; you must not
  16. // claim that you wrote the original software. If you use this software
  17. // in a product, an acknowledgment in the product documentation would
  18. // be appreciated but is not required.
  19. //
  20. // 2. Altered source versions must be plainly marked as such, and must not
  21. // be misrepresented as being the original software.
  22. //
  23. // 3. This notice may not be removed or altered from any source
  24. // distribution.
  25. //
  26. //========================================================================
  27. // It is fine to use C99 in this file because it will not be built with VS
  28. //========================================================================
  29. #include "internal.h"
  30. #include <limits.h>
  31. #include <stdlib.h>
  32. #include <string.h>
  33. #include <math.h>
  34. // Check whether the display mode should be included in enumeration
  35. //
  36. static bool modeIsGood(const XRRModeInfo* mi)
  37. {
  38. return (mi->modeFlags & RR_Interlace) == 0;
  39. }
  40. // Calculates the refresh rate, in Hz, from the specified RandR mode info
  41. //
  42. static int calculateRefreshRate(const XRRModeInfo* mi)
  43. {
  44. if (mi->hTotal && mi->vTotal)
  45. return (int) round((double) mi->dotClock / ((double) mi->hTotal * (double) mi->vTotal));
  46. else
  47. return 0;
  48. }
  49. // Returns the mode info for a RandR mode XID
  50. //
  51. static const XRRModeInfo* getModeInfo(const XRRScreenResources* sr, RRMode id)
  52. {
  53. for (int i = 0; i < sr->nmode; i++)
  54. {
  55. if (sr->modes[i].id == id)
  56. return sr->modes + i;
  57. }
  58. return NULL;
  59. }
  60. // Convert RandR mode info to GLFW video mode
  61. //
  62. static GLFWvidmode vidmodeFromModeInfo(const XRRModeInfo* mi,
  63. const XRRCrtcInfo* ci)
  64. {
  65. GLFWvidmode mode;
  66. if (ci->rotation == RR_Rotate_90 || ci->rotation == RR_Rotate_270)
  67. {
  68. mode.width = mi->height;
  69. mode.height = mi->width;
  70. }
  71. else
  72. {
  73. mode.width = mi->width;
  74. mode.height = mi->height;
  75. }
  76. mode.refreshRate = calculateRefreshRate(mi);
  77. _glfwSplitBPP(DefaultDepth(_glfw.x11.display, _glfw.x11.screen),
  78. &mode.redBits, &mode.greenBits, &mode.blueBits);
  79. return mode;
  80. }
  81. //////////////////////////////////////////////////////////////////////////
  82. ////// GLFW internal API //////
  83. //////////////////////////////////////////////////////////////////////////
  84. // Poll for changes in the set of connected monitors
  85. //
  86. void _glfwPollMonitorsX11(void)
  87. {
  88. if (_glfw.x11.randr.available && !_glfw.x11.randr.monitorBroken)
  89. {
  90. int disconnectedCount, screenCount = 0;
  91. _GLFWmonitor** disconnected = NULL;
  92. XineramaScreenInfo* screens = NULL;
  93. XRRScreenResources* sr = XRRGetScreenResourcesCurrent(_glfw.x11.display,
  94. _glfw.x11.root);
  95. RROutput primary = XRRGetOutputPrimary(_glfw.x11.display,
  96. _glfw.x11.root);
  97. if (_glfw.x11.xinerama.available)
  98. screens = XineramaQueryScreens(_glfw.x11.display, &screenCount);
  99. disconnectedCount = _glfw.monitorCount;
  100. if (disconnectedCount)
  101. {
  102. disconnected = calloc(_glfw.monitorCount, sizeof(_GLFWmonitor*));
  103. memcpy(disconnected,
  104. _glfw.monitors,
  105. _glfw.monitorCount * sizeof(_GLFWmonitor*));
  106. }
  107. for (int i = 0; i < sr->noutput; i++)
  108. {
  109. int j, type, widthMM, heightMM;
  110. XRROutputInfo* oi = XRRGetOutputInfo(_glfw.x11.display, sr, sr->outputs[i]);
  111. if (oi->connection != RR_Connected || oi->crtc == None)
  112. {
  113. XRRFreeOutputInfo(oi);
  114. continue;
  115. }
  116. for (j = 0; j < disconnectedCount; j++)
  117. {
  118. if (disconnected[j] &&
  119. disconnected[j]->x11.output == sr->outputs[i])
  120. {
  121. disconnected[j] = NULL;
  122. break;
  123. }
  124. }
  125. if (j < disconnectedCount)
  126. {
  127. XRRFreeOutputInfo(oi);
  128. continue;
  129. }
  130. XRRCrtcInfo* ci = XRRGetCrtcInfo(_glfw.x11.display, sr, oi->crtc);
  131. if (!ci)
  132. {
  133. XRRFreeOutputInfo(oi);
  134. continue;
  135. }
  136. if (ci->rotation == RR_Rotate_90 || ci->rotation == RR_Rotate_270)
  137. {
  138. widthMM = oi->mm_height;
  139. heightMM = oi->mm_width;
  140. }
  141. else
  142. {
  143. widthMM = oi->mm_width;
  144. heightMM = oi->mm_height;
  145. }
  146. if (widthMM <= 0 || heightMM <= 0)
  147. {
  148. // HACK: If RandR does not provide a physical size, assume the
  149. // X11 default 96 DPI and calculate from the CRTC viewport
  150. // NOTE: These members are affected by rotation, unlike the mode
  151. // info and output info members
  152. widthMM = (int) (ci->width * 25.4f / 96.f);
  153. heightMM = (int) (ci->height * 25.4f / 96.f);
  154. }
  155. _GLFWmonitor* monitor = _glfwAllocMonitor(oi->name, widthMM, heightMM);
  156. monitor->x11.output = sr->outputs[i];
  157. monitor->x11.crtc = oi->crtc;
  158. for (j = 0; j < screenCount; j++)
  159. {
  160. if (screens[j].x_org == ci->x &&
  161. screens[j].y_org == ci->y &&
  162. screens[j].width == (short int)ci->width &&
  163. screens[j].height == (short int)ci->height)
  164. {
  165. monitor->x11.index = j;
  166. break;
  167. }
  168. }
  169. if (monitor->x11.output == primary)
  170. type = _GLFW_INSERT_FIRST;
  171. else
  172. type = _GLFW_INSERT_LAST;
  173. _glfwInputMonitor(monitor, GLFW_CONNECTED, type);
  174. XRRFreeOutputInfo(oi);
  175. XRRFreeCrtcInfo(ci);
  176. }
  177. XRRFreeScreenResources(sr);
  178. if (screens)
  179. XFree(screens);
  180. for (int i = 0; i < disconnectedCount; i++)
  181. {
  182. if (disconnected[i])
  183. _glfwInputMonitor(disconnected[i], GLFW_DISCONNECTED, 0);
  184. }
  185. free(disconnected);
  186. }
  187. else
  188. {
  189. const int widthMM = DisplayWidthMM(_glfw.x11.display, _glfw.x11.screen);
  190. const int heightMM = DisplayHeightMM(_glfw.x11.display, _glfw.x11.screen);
  191. _glfwInputMonitor(_glfwAllocMonitor("Display", widthMM, heightMM),
  192. GLFW_CONNECTED,
  193. _GLFW_INSERT_FIRST);
  194. }
  195. }
  196. // Set the current video mode for the specified monitor
  197. //
  198. void _glfwSetVideoModeX11(_GLFWmonitor* monitor, const GLFWvidmode* desired)
  199. {
  200. if (_glfw.x11.randr.available && !_glfw.x11.randr.monitorBroken)
  201. {
  202. GLFWvidmode current;
  203. RRMode native = None;
  204. const GLFWvidmode* best = _glfwChooseVideoMode(monitor, desired);
  205. _glfwPlatformGetVideoMode(monitor, &current);
  206. if (_glfwCompareVideoModes(&current, best) == 0)
  207. return;
  208. XRRScreenResources* sr =
  209. XRRGetScreenResourcesCurrent(_glfw.x11.display, _glfw.x11.root);
  210. XRRCrtcInfo* ci = XRRGetCrtcInfo(_glfw.x11.display, sr, monitor->x11.crtc);
  211. XRROutputInfo* oi = XRRGetOutputInfo(_glfw.x11.display, sr, monitor->x11.output);
  212. for (int i = 0; i < oi->nmode; i++)
  213. {
  214. const XRRModeInfo* mi = getModeInfo(sr, oi->modes[i]);
  215. if (!modeIsGood(mi))
  216. continue;
  217. const GLFWvidmode mode = vidmodeFromModeInfo(mi, ci);
  218. if (_glfwCompareVideoModes(best, &mode) == 0)
  219. {
  220. native = mi->id;
  221. break;
  222. }
  223. }
  224. if (native)
  225. {
  226. if (monitor->x11.oldMode == None)
  227. monitor->x11.oldMode = ci->mode;
  228. XRRSetCrtcConfig(_glfw.x11.display,
  229. sr, monitor->x11.crtc,
  230. CurrentTime,
  231. ci->x, ci->y,
  232. native,
  233. ci->rotation,
  234. ci->outputs,
  235. ci->noutput);
  236. }
  237. XRRFreeOutputInfo(oi);
  238. XRRFreeCrtcInfo(ci);
  239. XRRFreeScreenResources(sr);
  240. }
  241. }
  242. // Restore the saved (original) video mode for the specified monitor
  243. //
  244. void _glfwRestoreVideoModeX11(_GLFWmonitor* monitor)
  245. {
  246. if (_glfw.x11.randr.available && !_glfw.x11.randr.monitorBroken)
  247. {
  248. if (monitor->x11.oldMode == None)
  249. return;
  250. XRRScreenResources* sr =
  251. XRRGetScreenResourcesCurrent(_glfw.x11.display, _glfw.x11.root);
  252. XRRCrtcInfo* ci = XRRGetCrtcInfo(_glfw.x11.display, sr, monitor->x11.crtc);
  253. XRRSetCrtcConfig(_glfw.x11.display,
  254. sr, monitor->x11.crtc,
  255. CurrentTime,
  256. ci->x, ci->y,
  257. monitor->x11.oldMode,
  258. ci->rotation,
  259. ci->outputs,
  260. ci->noutput);
  261. XRRFreeCrtcInfo(ci);
  262. XRRFreeScreenResources(sr);
  263. monitor->x11.oldMode = None;
  264. }
  265. }
  266. //////////////////////////////////////////////////////////////////////////
  267. ////// GLFW platform API //////
  268. //////////////////////////////////////////////////////////////////////////
  269. void _glfwPlatformFreeMonitor(_GLFWmonitor* monitor UNUSED)
  270. {
  271. }
  272. void _glfwPlatformGetMonitorPos(_GLFWmonitor* monitor, int* xpos, int* ypos)
  273. {
  274. if (_glfw.x11.randr.available && !_glfw.x11.randr.monitorBroken)
  275. {
  276. XRRScreenResources* sr =
  277. XRRGetScreenResourcesCurrent(_glfw.x11.display, _glfw.x11.root);
  278. XRRCrtcInfo* ci = XRRGetCrtcInfo(_glfw.x11.display, sr, monitor->x11.crtc);
  279. if (ci)
  280. {
  281. if (xpos)
  282. *xpos = ci->x;
  283. if (ypos)
  284. *ypos = ci->y;
  285. XRRFreeCrtcInfo(ci);
  286. }
  287. XRRFreeScreenResources(sr);
  288. }
  289. }
  290. void _glfwPlatformGetMonitorContentScale(_GLFWmonitor* monitor UNUSED,
  291. float* xscale, float* yscale)
  292. {
  293. if (xscale)
  294. *xscale = _glfw.x11.contentScaleX;
  295. if (yscale)
  296. *yscale = _glfw.x11.contentScaleY;
  297. }
  298. void _glfwPlatformGetMonitorWorkarea(_GLFWmonitor* monitor, int* xpos, int* ypos, int* width, int* height)
  299. {
  300. int areaX = 0, areaY = 0, areaWidth = 0, areaHeight = 0;
  301. if (_glfw.x11.randr.available && !_glfw.x11.randr.monitorBroken)
  302. {
  303. XRRScreenResources* sr =
  304. XRRGetScreenResourcesCurrent(_glfw.x11.display, _glfw.x11.root);
  305. XRRCrtcInfo* ci = XRRGetCrtcInfo(_glfw.x11.display, sr, monitor->x11.crtc);
  306. areaX = ci->x;
  307. areaY = ci->y;
  308. const XRRModeInfo* mi = getModeInfo(sr, ci->mode);
  309. if (ci->rotation == RR_Rotate_90 || ci->rotation == RR_Rotate_270)
  310. {
  311. areaWidth = mi->height;
  312. areaHeight = mi->width;
  313. }
  314. else
  315. {
  316. areaWidth = mi->width;
  317. areaHeight = mi->height;
  318. }
  319. XRRFreeCrtcInfo(ci);
  320. XRRFreeScreenResources(sr);
  321. }
  322. else
  323. {
  324. areaWidth = DisplayWidth(_glfw.x11.display, _glfw.x11.screen);
  325. areaHeight = DisplayHeight(_glfw.x11.display, _glfw.x11.screen);
  326. }
  327. if (_glfw.x11.NET_WORKAREA && _glfw.x11.NET_CURRENT_DESKTOP)
  328. {
  329. Atom* extents = NULL;
  330. Atom* desktop = NULL;
  331. const unsigned long extentCount =
  332. _glfwGetWindowPropertyX11(_glfw.x11.root,
  333. _glfw.x11.NET_WORKAREA,
  334. XA_CARDINAL,
  335. (unsigned char**) &extents);
  336. if (_glfwGetWindowPropertyX11(_glfw.x11.root,
  337. _glfw.x11.NET_CURRENT_DESKTOP,
  338. XA_CARDINAL,
  339. (unsigned char**) &desktop) > 0)
  340. {
  341. if (extentCount >= 4 && *desktop < extentCount / 4)
  342. {
  343. const int globalX = extents[*desktop * 4 + 0];
  344. const int globalY = extents[*desktop * 4 + 1];
  345. const int globalWidth = extents[*desktop * 4 + 2];
  346. const int globalHeight = extents[*desktop * 4 + 3];
  347. if (areaX < globalX)
  348. {
  349. areaWidth -= globalX - areaX;
  350. areaX = globalX;
  351. }
  352. if (areaY < globalY)
  353. {
  354. areaHeight -= globalY - areaY;
  355. areaY = globalY;
  356. }
  357. if (areaX + areaWidth > globalX + globalWidth)
  358. areaWidth = globalX - areaX + globalWidth;
  359. if (areaY + areaHeight > globalY + globalHeight)
  360. areaHeight = globalY - areaY + globalHeight;
  361. }
  362. }
  363. if (extents)
  364. XFree(extents);
  365. if (desktop)
  366. XFree(desktop);
  367. }
  368. if (xpos)
  369. *xpos = areaX;
  370. if (ypos)
  371. *ypos = areaY;
  372. if (width)
  373. *width = areaWidth;
  374. if (height)
  375. *height = areaHeight;
  376. }
  377. GLFWvidmode* _glfwPlatformGetVideoModes(_GLFWmonitor* monitor, int* count)
  378. {
  379. GLFWvidmode* result;
  380. *count = 0;
  381. if (_glfw.x11.randr.available && !_glfw.x11.randr.monitorBroken)
  382. {
  383. XRRScreenResources* sr =
  384. XRRGetScreenResourcesCurrent(_glfw.x11.display, _glfw.x11.root);
  385. XRRCrtcInfo* ci = XRRGetCrtcInfo(_glfw.x11.display, sr, monitor->x11.crtc);
  386. XRROutputInfo* oi = XRRGetOutputInfo(_glfw.x11.display, sr, monitor->x11.output);
  387. result = calloc(oi->nmode, sizeof(GLFWvidmode));
  388. for (int i = 0; i < oi->nmode; i++)
  389. {
  390. const XRRModeInfo* mi = getModeInfo(sr, oi->modes[i]);
  391. if (!modeIsGood(mi))
  392. continue;
  393. const GLFWvidmode mode = vidmodeFromModeInfo(mi, ci);
  394. int j;
  395. for (j = 0; j < *count; j++)
  396. {
  397. if (_glfwCompareVideoModes(result + j, &mode) == 0)
  398. break;
  399. }
  400. // Skip duplicate modes
  401. if (j < *count)
  402. continue;
  403. (*count)++;
  404. result[*count - 1] = mode;
  405. }
  406. XRRFreeOutputInfo(oi);
  407. XRRFreeCrtcInfo(ci);
  408. XRRFreeScreenResources(sr);
  409. }
  410. else
  411. {
  412. *count = 1;
  413. result = calloc(1, sizeof(GLFWvidmode));
  414. _glfwPlatformGetVideoMode(monitor, result);
  415. }
  416. return result;
  417. }
  418. bool _glfwPlatformGetVideoMode(_GLFWmonitor* monitor, GLFWvidmode* mode) {
  419. bool ok = false;
  420. if (_glfw.x11.randr.available && !_glfw.x11.randr.monitorBroken)
  421. {
  422. XRRScreenResources* sr =
  423. XRRGetScreenResourcesCurrent(_glfw.x11.display, _glfw.x11.root);
  424. XRRCrtcInfo* ci = XRRGetCrtcInfo(_glfw.x11.display, sr, monitor->x11.crtc);
  425. if (ci)
  426. {
  427. const XRRModeInfo* mi = getModeInfo(sr, ci->mode);
  428. if (mi) { // mi can be NULL if the monitor has been disconnected
  429. *mode = vidmodeFromModeInfo(mi, ci);
  430. ok = true;
  431. }
  432. XRRFreeCrtcInfo(ci);
  433. }
  434. XRRFreeScreenResources(sr);
  435. }
  436. else
  437. {
  438. ok = true;
  439. mode->width = DisplayWidth(_glfw.x11.display, _glfw.x11.screen);
  440. mode->height = DisplayHeight(_glfw.x11.display, _glfw.x11.screen);
  441. mode->refreshRate = 0;
  442. _glfwSplitBPP(DefaultDepth(_glfw.x11.display, _glfw.x11.screen),
  443. &mode->redBits, &mode->greenBits, &mode->blueBits);
  444. }
  445. return ok;
  446. }
  447. bool _glfwPlatformGetGammaRamp(_GLFWmonitor* monitor, GLFWgammaramp* ramp)
  448. {
  449. if (_glfw.x11.randr.available && !_glfw.x11.randr.gammaBroken)
  450. {
  451. const size_t size = XRRGetCrtcGammaSize(_glfw.x11.display,
  452. monitor->x11.crtc);
  453. XRRCrtcGamma* gamma = XRRGetCrtcGamma(_glfw.x11.display,
  454. monitor->x11.crtc);
  455. _glfwAllocGammaArrays(ramp, size);
  456. memcpy(ramp->red, gamma->red, size * sizeof(unsigned short));
  457. memcpy(ramp->green, gamma->green, size * sizeof(unsigned short));
  458. memcpy(ramp->blue, gamma->blue, size * sizeof(unsigned short));
  459. XRRFreeGamma(gamma);
  460. return true;
  461. }
  462. else if (_glfw.x11.vidmode.available)
  463. {
  464. int size;
  465. XF86VidModeGetGammaRampSize(_glfw.x11.display, _glfw.x11.screen, &size);
  466. _glfwAllocGammaArrays(ramp, size);
  467. XF86VidModeGetGammaRamp(_glfw.x11.display,
  468. _glfw.x11.screen,
  469. ramp->size, ramp->red, ramp->green, ramp->blue);
  470. return true;
  471. }
  472. else
  473. {
  474. _glfwInputError(GLFW_PLATFORM_ERROR,
  475. "X11: Gamma ramp access not supported by server");
  476. return false;
  477. }
  478. }
  479. void _glfwPlatformSetGammaRamp(_GLFWmonitor* monitor, const GLFWgammaramp* ramp)
  480. {
  481. if (_glfw.x11.randr.available && !_glfw.x11.randr.gammaBroken)
  482. {
  483. if (XRRGetCrtcGammaSize(_glfw.x11.display, monitor->x11.crtc) != (int)ramp->size)
  484. {
  485. _glfwInputError(GLFW_PLATFORM_ERROR,
  486. "X11: Gamma ramp size must match current ramp size");
  487. return;
  488. }
  489. XRRCrtcGamma* gamma = XRRAllocGamma(ramp->size);
  490. memcpy(gamma->red, ramp->red, ramp->size * sizeof(unsigned short));
  491. memcpy(gamma->green, ramp->green, ramp->size * sizeof(unsigned short));
  492. memcpy(gamma->blue, ramp->blue, ramp->size * sizeof(unsigned short));
  493. XRRSetCrtcGamma(_glfw.x11.display, monitor->x11.crtc, gamma);
  494. XRRFreeGamma(gamma);
  495. }
  496. else if (_glfw.x11.vidmode.available)
  497. {
  498. XF86VidModeSetGammaRamp(_glfw.x11.display,
  499. _glfw.x11.screen,
  500. ramp->size,
  501. (unsigned short*) ramp->red,
  502. (unsigned short*) ramp->green,
  503. (unsigned short*) ramp->blue);
  504. }
  505. else
  506. {
  507. _glfwInputError(GLFW_PLATFORM_ERROR,
  508. "X11: Gamma ramp access not supported by server");
  509. }
  510. }
  511. //////////////////////////////////////////////////////////////////////////
  512. ////// GLFW native API //////
  513. //////////////////////////////////////////////////////////////////////////
  514. GLFWAPI RRCrtc glfwGetX11Adapter(GLFWmonitor* handle)
  515. {
  516. _GLFWmonitor* monitor = (_GLFWmonitor*) handle;
  517. _GLFW_REQUIRE_INIT_OR_RETURN(None);
  518. return monitor->x11.crtc;
  519. }
  520. GLFWAPI RROutput glfwGetX11Monitor(GLFWmonitor* handle)
  521. {
  522. _GLFWmonitor* monitor = (_GLFWmonitor*) handle;
  523. _GLFW_REQUIRE_INIT_OR_RETURN(None);
  524. return monitor->x11.output;
  525. }