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