x11_monitor.c (20136B)
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 28 #include "internal.h" 29 30 #if defined(_GLFW_X11) 31 32 #include <limits.h> 33 #include <stdlib.h> 34 #include <string.h> 35 #include <math.h> 36 37 38 // Check whether the display mode should be included in enumeration 39 // 40 static GLFWbool modeIsGood(const XRRModeInfo* mi) 41 { 42 return (mi->modeFlags & RR_Interlace) == 0; 43 } 44 45 // Calculates the refresh rate, in Hz, from the specified RandR mode info 46 // 47 static int calculateRefreshRate(const XRRModeInfo* mi) 48 { 49 if (mi->hTotal && mi->vTotal) 50 return (int) round((double) mi->dotClock / ((double) mi->hTotal * (double) mi->vTotal)); 51 else 52 return 0; 53 } 54 55 // Returns the mode info for a RandR mode XID 56 // 57 static const XRRModeInfo* getModeInfo(const XRRScreenResources* sr, RRMode id) 58 { 59 for (int i = 0; i < sr->nmode; i++) 60 { 61 if (sr->modes[i].id == id) 62 return sr->modes + i; 63 } 64 65 return NULL; 66 } 67 68 // Convert RandR mode info to GLFW video mode 69 // 70 static GLFWvidmode vidmodeFromModeInfo(const XRRModeInfo* mi, 71 const XRRCrtcInfo* ci) 72 { 73 GLFWvidmode mode; 74 75 if (ci->rotation == RR_Rotate_90 || ci->rotation == RR_Rotate_270) 76 { 77 mode.width = mi->height; 78 mode.height = mi->width; 79 } 80 else 81 { 82 mode.width = mi->width; 83 mode.height = mi->height; 84 } 85 86 mode.refreshRate = calculateRefreshRate(mi); 87 88 _glfwSplitBPP(DefaultDepth(_glfw.x11.display, _glfw.x11.screen), 89 &mode.redBits, &mode.greenBits, &mode.blueBits); 90 91 return mode; 92 } 93 94 95 ////////////////////////////////////////////////////////////////////////// 96 ////// GLFW internal API ////// 97 ////////////////////////////////////////////////////////////////////////// 98 99 // Poll for changes in the set of connected monitors 100 // 101 void _glfwPollMonitorsX11(void) 102 { 103 if (_glfw.x11.randr.available && !_glfw.x11.randr.monitorBroken) 104 { 105 int disconnectedCount, screenCount = 0; 106 _GLFWmonitor** disconnected = NULL; 107 XineramaScreenInfo* screens = NULL; 108 XRRScreenResources* sr = XRRGetScreenResourcesCurrent(_glfw.x11.display, 109 _glfw.x11.root); 110 RROutput primary = XRRGetOutputPrimary(_glfw.x11.display, 111 _glfw.x11.root); 112 113 if (_glfw.x11.xinerama.available) 114 screens = XineramaQueryScreens(_glfw.x11.display, &screenCount); 115 116 disconnectedCount = _glfw.monitorCount; 117 if (disconnectedCount) 118 { 119 disconnected = _glfw_calloc(_glfw.monitorCount, sizeof(_GLFWmonitor*)); 120 memcpy(disconnected, 121 _glfw.monitors, 122 _glfw.monitorCount * sizeof(_GLFWmonitor*)); 123 } 124 125 for (int i = 0; i < sr->noutput; i++) 126 { 127 int j, type, widthMM, heightMM; 128 129 XRROutputInfo* oi = XRRGetOutputInfo(_glfw.x11.display, sr, sr->outputs[i]); 130 if (oi->connection != RR_Connected || oi->crtc == None) 131 { 132 XRRFreeOutputInfo(oi); 133 continue; 134 } 135 136 for (j = 0; j < disconnectedCount; j++) 137 { 138 if (disconnected[j] && 139 disconnected[j]->x11.output == sr->outputs[i]) 140 { 141 disconnected[j] = NULL; 142 break; 143 } 144 } 145 146 if (j < disconnectedCount) 147 { 148 XRRFreeOutputInfo(oi); 149 continue; 150 } 151 152 XRRCrtcInfo* ci = XRRGetCrtcInfo(_glfw.x11.display, sr, oi->crtc); 153 if (ci->rotation == RR_Rotate_90 || ci->rotation == RR_Rotate_270) 154 { 155 widthMM = oi->mm_height; 156 heightMM = oi->mm_width; 157 } 158 else 159 { 160 widthMM = oi->mm_width; 161 heightMM = oi->mm_height; 162 } 163 164 if (widthMM <= 0 || heightMM <= 0) 165 { 166 // HACK: If RandR does not provide a physical size, assume the 167 // X11 default 96 DPI and calculate from the CRTC viewport 168 // NOTE: These members are affected by rotation, unlike the mode 169 // info and output info members 170 widthMM = (int) (ci->width * 25.4f / 96.f); 171 heightMM = (int) (ci->height * 25.4f / 96.f); 172 } 173 174 _GLFWmonitor* monitor = _glfwAllocMonitor(oi->name, widthMM, heightMM); 175 monitor->x11.output = sr->outputs[i]; 176 monitor->x11.crtc = oi->crtc; 177 178 for (j = 0; j < screenCount; j++) 179 { 180 if (screens[j].x_org == ci->x && 181 screens[j].y_org == ci->y && 182 screens[j].width == ci->width && 183 screens[j].height == ci->height) 184 { 185 monitor->x11.index = j; 186 break; 187 } 188 } 189 190 if (monitor->x11.output == primary) 191 type = _GLFW_INSERT_FIRST; 192 else 193 type = _GLFW_INSERT_LAST; 194 195 _glfwInputMonitor(monitor, GLFW_CONNECTED, type); 196 197 XRRFreeOutputInfo(oi); 198 XRRFreeCrtcInfo(ci); 199 } 200 201 XRRFreeScreenResources(sr); 202 203 if (screens) 204 XFree(screens); 205 206 for (int i = 0; i < disconnectedCount; i++) 207 { 208 if (disconnected[i]) 209 _glfwInputMonitor(disconnected[i], GLFW_DISCONNECTED, 0); 210 } 211 212 _glfw_free(disconnected); 213 } 214 else 215 { 216 const int widthMM = DisplayWidthMM(_glfw.x11.display, _glfw.x11.screen); 217 const int heightMM = DisplayHeightMM(_glfw.x11.display, _glfw.x11.screen); 218 219 _glfwInputMonitor(_glfwAllocMonitor("Display", widthMM, heightMM), 220 GLFW_CONNECTED, 221 _GLFW_INSERT_FIRST); 222 } 223 } 224 225 // Set the current video mode for the specified monitor 226 // 227 void _glfwSetVideoModeX11(_GLFWmonitor* monitor, const GLFWvidmode* desired) 228 { 229 if (_glfw.x11.randr.available && !_glfw.x11.randr.monitorBroken) 230 { 231 GLFWvidmode current; 232 RRMode native = None; 233 234 const GLFWvidmode* best = _glfwChooseVideoMode(monitor, desired); 235 _glfwGetVideoModeX11(monitor, ¤t); 236 if (_glfwCompareVideoModes(¤t, best) == 0) 237 return; 238 239 XRRScreenResources* sr = 240 XRRGetScreenResourcesCurrent(_glfw.x11.display, _glfw.x11.root); 241 XRRCrtcInfo* ci = XRRGetCrtcInfo(_glfw.x11.display, sr, monitor->x11.crtc); 242 XRROutputInfo* oi = XRRGetOutputInfo(_glfw.x11.display, sr, monitor->x11.output); 243 244 for (int i = 0; i < oi->nmode; i++) 245 { 246 const XRRModeInfo* mi = getModeInfo(sr, oi->modes[i]); 247 if (!modeIsGood(mi)) 248 continue; 249 250 const GLFWvidmode mode = vidmodeFromModeInfo(mi, ci); 251 if (_glfwCompareVideoModes(best, &mode) == 0) 252 { 253 native = mi->id; 254 break; 255 } 256 } 257 258 if (native) 259 { 260 if (monitor->x11.oldMode == None) 261 monitor->x11.oldMode = ci->mode; 262 263 XRRSetCrtcConfig(_glfw.x11.display, 264 sr, monitor->x11.crtc, 265 CurrentTime, 266 ci->x, ci->y, 267 native, 268 ci->rotation, 269 ci->outputs, 270 ci->noutput); 271 } 272 273 XRRFreeOutputInfo(oi); 274 XRRFreeCrtcInfo(ci); 275 XRRFreeScreenResources(sr); 276 } 277 } 278 279 // Restore the saved (original) video mode for the specified monitor 280 // 281 void _glfwRestoreVideoModeX11(_GLFWmonitor* monitor) 282 { 283 if (_glfw.x11.randr.available && !_glfw.x11.randr.monitorBroken) 284 { 285 if (monitor->x11.oldMode == None) 286 return; 287 288 XRRScreenResources* sr = 289 XRRGetScreenResourcesCurrent(_glfw.x11.display, _glfw.x11.root); 290 XRRCrtcInfo* ci = XRRGetCrtcInfo(_glfw.x11.display, sr, monitor->x11.crtc); 291 292 XRRSetCrtcConfig(_glfw.x11.display, 293 sr, monitor->x11.crtc, 294 CurrentTime, 295 ci->x, ci->y, 296 monitor->x11.oldMode, 297 ci->rotation, 298 ci->outputs, 299 ci->noutput); 300 301 XRRFreeCrtcInfo(ci); 302 XRRFreeScreenResources(sr); 303 304 monitor->x11.oldMode = None; 305 } 306 } 307 308 309 ////////////////////////////////////////////////////////////////////////// 310 ////// GLFW platform API ////// 311 ////////////////////////////////////////////////////////////////////////// 312 313 void _glfwFreeMonitorX11(_GLFWmonitor* monitor) 314 { 315 } 316 317 void _glfwGetMonitorPosX11(_GLFWmonitor* monitor, int* xpos, int* ypos) 318 { 319 if (_glfw.x11.randr.available && !_glfw.x11.randr.monitorBroken) 320 { 321 XRRScreenResources* sr = 322 XRRGetScreenResourcesCurrent(_glfw.x11.display, _glfw.x11.root); 323 XRRCrtcInfo* ci = XRRGetCrtcInfo(_glfw.x11.display, sr, monitor->x11.crtc); 324 325 if (ci) 326 { 327 if (xpos) 328 *xpos = ci->x; 329 if (ypos) 330 *ypos = ci->y; 331 332 XRRFreeCrtcInfo(ci); 333 } 334 335 XRRFreeScreenResources(sr); 336 } 337 } 338 339 void _glfwGetMonitorContentScaleX11(_GLFWmonitor* monitor, 340 float* xscale, float* yscale) 341 { 342 if (xscale) 343 *xscale = _glfw.x11.contentScaleX; 344 if (yscale) 345 *yscale = _glfw.x11.contentScaleY; 346 } 347 348 void _glfwGetMonitorWorkareaX11(_GLFWmonitor* monitor, 349 int* xpos, int* ypos, 350 int* width, int* height) 351 { 352 int areaX = 0, areaY = 0, areaWidth = 0, areaHeight = 0; 353 354 if (_glfw.x11.randr.available && !_glfw.x11.randr.monitorBroken) 355 { 356 XRRScreenResources* sr = 357 XRRGetScreenResourcesCurrent(_glfw.x11.display, _glfw.x11.root); 358 XRRCrtcInfo* ci = XRRGetCrtcInfo(_glfw.x11.display, sr, monitor->x11.crtc); 359 360 areaX = ci->x; 361 areaY = ci->y; 362 363 const XRRModeInfo* mi = getModeInfo(sr, ci->mode); 364 365 if (ci->rotation == RR_Rotate_90 || ci->rotation == RR_Rotate_270) 366 { 367 areaWidth = mi->height; 368 areaHeight = mi->width; 369 } 370 else 371 { 372 areaWidth = mi->width; 373 areaHeight = mi->height; 374 } 375 376 XRRFreeCrtcInfo(ci); 377 XRRFreeScreenResources(sr); 378 } 379 else 380 { 381 areaWidth = DisplayWidth(_glfw.x11.display, _glfw.x11.screen); 382 areaHeight = DisplayHeight(_glfw.x11.display, _glfw.x11.screen); 383 } 384 385 if (_glfw.x11.NET_WORKAREA && _glfw.x11.NET_CURRENT_DESKTOP) 386 { 387 Atom* extents = NULL; 388 Atom* desktop = NULL; 389 const unsigned long extentCount = 390 _glfwGetWindowPropertyX11(_glfw.x11.root, 391 _glfw.x11.NET_WORKAREA, 392 XA_CARDINAL, 393 (unsigned char**) &extents); 394 395 if (_glfwGetWindowPropertyX11(_glfw.x11.root, 396 _glfw.x11.NET_CURRENT_DESKTOP, 397 XA_CARDINAL, 398 (unsigned char**) &desktop) > 0) 399 { 400 if (extentCount >= 4 && *desktop < extentCount / 4) 401 { 402 const int globalX = extents[*desktop * 4 + 0]; 403 const int globalY = extents[*desktop * 4 + 1]; 404 const int globalWidth = extents[*desktop * 4 + 2]; 405 const int globalHeight = extents[*desktop * 4 + 3]; 406 407 if (areaX < globalX) 408 { 409 areaWidth -= globalX - areaX; 410 areaX = globalX; 411 } 412 413 if (areaY < globalY) 414 { 415 areaHeight -= globalY - areaY; 416 areaY = globalY; 417 } 418 419 if (areaX + areaWidth > globalX + globalWidth) 420 areaWidth = globalX - areaX + globalWidth; 421 if (areaY + areaHeight > globalY + globalHeight) 422 areaHeight = globalY - areaY + globalHeight; 423 } 424 } 425 426 if (extents) 427 XFree(extents); 428 if (desktop) 429 XFree(desktop); 430 } 431 432 if (xpos) 433 *xpos = areaX; 434 if (ypos) 435 *ypos = areaY; 436 if (width) 437 *width = areaWidth; 438 if (height) 439 *height = areaHeight; 440 } 441 442 GLFWvidmode* _glfwGetVideoModesX11(_GLFWmonitor* monitor, int* count) 443 { 444 GLFWvidmode* result; 445 446 *count = 0; 447 448 if (_glfw.x11.randr.available && !_glfw.x11.randr.monitorBroken) 449 { 450 XRRScreenResources* sr = 451 XRRGetScreenResourcesCurrent(_glfw.x11.display, _glfw.x11.root); 452 XRRCrtcInfo* ci = XRRGetCrtcInfo(_glfw.x11.display, sr, monitor->x11.crtc); 453 XRROutputInfo* oi = XRRGetOutputInfo(_glfw.x11.display, sr, monitor->x11.output); 454 455 result = _glfw_calloc(oi->nmode, sizeof(GLFWvidmode)); 456 457 for (int i = 0; i < oi->nmode; i++) 458 { 459 const XRRModeInfo* mi = getModeInfo(sr, oi->modes[i]); 460 if (!modeIsGood(mi)) 461 continue; 462 463 const GLFWvidmode mode = vidmodeFromModeInfo(mi, ci); 464 int j; 465 466 for (j = 0; j < *count; j++) 467 { 468 if (_glfwCompareVideoModes(result + j, &mode) == 0) 469 break; 470 } 471 472 // Skip duplicate modes 473 if (j < *count) 474 continue; 475 476 (*count)++; 477 result[*count - 1] = mode; 478 } 479 480 XRRFreeOutputInfo(oi); 481 XRRFreeCrtcInfo(ci); 482 XRRFreeScreenResources(sr); 483 } 484 else 485 { 486 *count = 1; 487 result = _glfw_calloc(1, sizeof(GLFWvidmode)); 488 _glfwGetVideoModeX11(monitor, result); 489 } 490 491 return result; 492 } 493 494 GLFWbool _glfwGetVideoModeX11(_GLFWmonitor* monitor, GLFWvidmode* mode) 495 { 496 if (_glfw.x11.randr.available && !_glfw.x11.randr.monitorBroken) 497 { 498 XRRScreenResources* sr = 499 XRRGetScreenResourcesCurrent(_glfw.x11.display, _glfw.x11.root); 500 const XRRModeInfo* mi = NULL; 501 502 XRRCrtcInfo* ci = XRRGetCrtcInfo(_glfw.x11.display, sr, monitor->x11.crtc); 503 if (ci) 504 { 505 mi = getModeInfo(sr, ci->mode); 506 if (mi) 507 *mode = vidmodeFromModeInfo(mi, ci); 508 509 XRRFreeCrtcInfo(ci); 510 } 511 512 XRRFreeScreenResources(sr); 513 514 if (!mi) 515 { 516 _glfwInputError(GLFW_PLATFORM_ERROR, "X11: Failed to query video mode"); 517 return GLFW_FALSE; 518 } 519 } 520 else 521 { 522 mode->width = DisplayWidth(_glfw.x11.display, _glfw.x11.screen); 523 mode->height = DisplayHeight(_glfw.x11.display, _glfw.x11.screen); 524 mode->refreshRate = 0; 525 526 _glfwSplitBPP(DefaultDepth(_glfw.x11.display, _glfw.x11.screen), 527 &mode->redBits, &mode->greenBits, &mode->blueBits); 528 } 529 530 return GLFW_TRUE; 531 } 532 533 GLFWbool _glfwGetGammaRampX11(_GLFWmonitor* monitor, GLFWgammaramp* ramp) 534 { 535 if (_glfw.x11.randr.available && !_glfw.x11.randr.gammaBroken) 536 { 537 const size_t size = XRRGetCrtcGammaSize(_glfw.x11.display, 538 monitor->x11.crtc); 539 XRRCrtcGamma* gamma = XRRGetCrtcGamma(_glfw.x11.display, 540 monitor->x11.crtc); 541 542 _glfwAllocGammaArrays(ramp, size); 543 544 memcpy(ramp->red, gamma->red, size * sizeof(unsigned short)); 545 memcpy(ramp->green, gamma->green, size * sizeof(unsigned short)); 546 memcpy(ramp->blue, gamma->blue, size * sizeof(unsigned short)); 547 548 XRRFreeGamma(gamma); 549 return GLFW_TRUE; 550 } 551 else if (_glfw.x11.vidmode.available) 552 { 553 int size; 554 XF86VidModeGetGammaRampSize(_glfw.x11.display, _glfw.x11.screen, &size); 555 556 _glfwAllocGammaArrays(ramp, size); 557 558 XF86VidModeGetGammaRamp(_glfw.x11.display, 559 _glfw.x11.screen, 560 ramp->size, ramp->red, ramp->green, ramp->blue); 561 return GLFW_TRUE; 562 } 563 else 564 { 565 _glfwInputError(GLFW_PLATFORM_ERROR, 566 "X11: Gamma ramp access not supported by server"); 567 return GLFW_FALSE; 568 } 569 } 570 571 void _glfwSetGammaRampX11(_GLFWmonitor* monitor, const GLFWgammaramp* ramp) 572 { 573 if (_glfw.x11.randr.available && !_glfw.x11.randr.gammaBroken) 574 { 575 if (XRRGetCrtcGammaSize(_glfw.x11.display, monitor->x11.crtc) != ramp->size) 576 { 577 _glfwInputError(GLFW_PLATFORM_ERROR, 578 "X11: Gamma ramp size must match current ramp size"); 579 return; 580 } 581 582 XRRCrtcGamma* gamma = XRRAllocGamma(ramp->size); 583 584 memcpy(gamma->red, ramp->red, ramp->size * sizeof(unsigned short)); 585 memcpy(gamma->green, ramp->green, ramp->size * sizeof(unsigned short)); 586 memcpy(gamma->blue, ramp->blue, ramp->size * sizeof(unsigned short)); 587 588 XRRSetCrtcGamma(_glfw.x11.display, monitor->x11.crtc, gamma); 589 XRRFreeGamma(gamma); 590 } 591 else if (_glfw.x11.vidmode.available) 592 { 593 XF86VidModeSetGammaRamp(_glfw.x11.display, 594 _glfw.x11.screen, 595 ramp->size, 596 (unsigned short*) ramp->red, 597 (unsigned short*) ramp->green, 598 (unsigned short*) ramp->blue); 599 } 600 else 601 { 602 _glfwInputError(GLFW_PLATFORM_ERROR, 603 "X11: Gamma ramp access not supported by server"); 604 } 605 } 606 607 608 ////////////////////////////////////////////////////////////////////////// 609 ////// GLFW native API ////// 610 ////////////////////////////////////////////////////////////////////////// 611 612 GLFWAPI RRCrtc glfwGetX11Adapter(GLFWmonitor* handle) 613 { 614 _GLFWmonitor* monitor = (_GLFWmonitor*) handle; 615 _GLFW_REQUIRE_INIT_OR_RETURN(None); 616 617 if (_glfw.platform.platformID != GLFW_PLATFORM_X11) 618 { 619 _glfwInputError(GLFW_PLATFORM_UNAVAILABLE, "X11: Platform not initialized"); 620 return None; 621 } 622 623 return monitor->x11.crtc; 624 } 625 626 GLFWAPI RROutput glfwGetX11Monitor(GLFWmonitor* handle) 627 { 628 _GLFWmonitor* monitor = (_GLFWmonitor*) handle; 629 _GLFW_REQUIRE_INIT_OR_RETURN(None); 630 631 if (_glfw.platform.platformID != GLFW_PLATFORM_X11) 632 { 633 _glfwInputError(GLFW_PLATFORM_UNAVAILABLE, "X11: Platform not initialized"); 634 return None; 635 } 636 637 return monitor->x11.output; 638 } 639 640 #endif // _GLFW_X11 641