x11_monitor.c (19711B)
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 30 #include "internal.h" 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 = 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 calcuate 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 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 _glfwPlatformGetVideoMode(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 _glfwPlatformFreeMonitor(_GLFWmonitor* monitor) 314 { 315 } 316 317 void _glfwPlatformGetMonitorPos(_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 _glfwPlatformGetMonitorContentScale(_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 _glfwPlatformGetMonitorWorkarea(_GLFWmonitor* monitor, int* xpos, int* ypos, int* width, int* height) 349 { 350 int areaX = 0, areaY = 0, areaWidth = 0, areaHeight = 0; 351 352 if (_glfw.x11.randr.available && !_glfw.x11.randr.monitorBroken) 353 { 354 XRRScreenResources* sr = 355 XRRGetScreenResourcesCurrent(_glfw.x11.display, _glfw.x11.root); 356 XRRCrtcInfo* ci = XRRGetCrtcInfo(_glfw.x11.display, sr, monitor->x11.crtc); 357 358 areaX = ci->x; 359 areaY = ci->y; 360 361 const XRRModeInfo* mi = getModeInfo(sr, ci->mode); 362 363 if (ci->rotation == RR_Rotate_90 || ci->rotation == RR_Rotate_270) 364 { 365 areaWidth = mi->height; 366 areaHeight = mi->width; 367 } 368 else 369 { 370 areaWidth = mi->width; 371 areaHeight = mi->height; 372 } 373 374 XRRFreeCrtcInfo(ci); 375 XRRFreeScreenResources(sr); 376 } 377 else 378 { 379 areaWidth = DisplayWidth(_glfw.x11.display, _glfw.x11.screen); 380 areaHeight = DisplayHeight(_glfw.x11.display, _glfw.x11.screen); 381 } 382 383 if (_glfw.x11.NET_WORKAREA && _glfw.x11.NET_CURRENT_DESKTOP) 384 { 385 Atom* extents = NULL; 386 Atom* desktop = NULL; 387 const unsigned long extentCount = 388 _glfwGetWindowPropertyX11(_glfw.x11.root, 389 _glfw.x11.NET_WORKAREA, 390 XA_CARDINAL, 391 (unsigned char**) &extents); 392 393 if (_glfwGetWindowPropertyX11(_glfw.x11.root, 394 _glfw.x11.NET_CURRENT_DESKTOP, 395 XA_CARDINAL, 396 (unsigned char**) &desktop) > 0) 397 { 398 if (extentCount >= 4 && *desktop < extentCount / 4) 399 { 400 const int globalX = extents[*desktop * 4 + 0]; 401 const int globalY = extents[*desktop * 4 + 1]; 402 const int globalWidth = extents[*desktop * 4 + 2]; 403 const int globalHeight = extents[*desktop * 4 + 3]; 404 405 if (areaX < globalX) 406 { 407 areaWidth -= globalX - areaX; 408 areaX = globalX; 409 } 410 411 if (areaY < globalY) 412 { 413 areaHeight -= globalY - areaY; 414 areaY = globalY; 415 } 416 417 if (areaX + areaWidth > globalX + globalWidth) 418 areaWidth = globalX - areaX + globalWidth; 419 if (areaY + areaHeight > globalY + globalHeight) 420 areaHeight = globalY - areaY + globalHeight; 421 } 422 } 423 424 if (extents) 425 XFree(extents); 426 if (desktop) 427 XFree(desktop); 428 } 429 430 if (xpos) 431 *xpos = areaX; 432 if (ypos) 433 *ypos = areaY; 434 if (width) 435 *width = areaWidth; 436 if (height) 437 *height = areaHeight; 438 } 439 440 GLFWvidmode* _glfwPlatformGetVideoModes(_GLFWmonitor* monitor, int* count) 441 { 442 GLFWvidmode* result; 443 444 *count = 0; 445 446 if (_glfw.x11.randr.available && !_glfw.x11.randr.monitorBroken) 447 { 448 XRRScreenResources* sr = 449 XRRGetScreenResourcesCurrent(_glfw.x11.display, _glfw.x11.root); 450 XRRCrtcInfo* ci = XRRGetCrtcInfo(_glfw.x11.display, sr, monitor->x11.crtc); 451 XRROutputInfo* oi = XRRGetOutputInfo(_glfw.x11.display, sr, monitor->x11.output); 452 453 result = calloc(oi->nmode, sizeof(GLFWvidmode)); 454 455 for (int i = 0; i < oi->nmode; i++) 456 { 457 const XRRModeInfo* mi = getModeInfo(sr, oi->modes[i]); 458 if (!modeIsGood(mi)) 459 continue; 460 461 const GLFWvidmode mode = vidmodeFromModeInfo(mi, ci); 462 int j; 463 464 for (j = 0; j < *count; j++) 465 { 466 if (_glfwCompareVideoModes(result + j, &mode) == 0) 467 break; 468 } 469 470 // Skip duplicate modes 471 if (j < *count) 472 continue; 473 474 (*count)++; 475 result[*count - 1] = mode; 476 } 477 478 XRRFreeOutputInfo(oi); 479 XRRFreeCrtcInfo(ci); 480 XRRFreeScreenResources(sr); 481 } 482 else 483 { 484 *count = 1; 485 result = calloc(1, sizeof(GLFWvidmode)); 486 _glfwPlatformGetVideoMode(monitor, result); 487 } 488 489 return result; 490 } 491 492 void _glfwPlatformGetVideoMode(_GLFWmonitor* monitor, GLFWvidmode* mode) 493 { 494 if (_glfw.x11.randr.available && !_glfw.x11.randr.monitorBroken) 495 { 496 XRRScreenResources* sr = 497 XRRGetScreenResourcesCurrent(_glfw.x11.display, _glfw.x11.root); 498 XRRCrtcInfo* ci = XRRGetCrtcInfo(_glfw.x11.display, sr, monitor->x11.crtc); 499 500 if (ci) 501 { 502 const XRRModeInfo* mi = getModeInfo(sr, ci->mode); 503 if (mi) // mi can be NULL if the monitor has been disconnected 504 *mode = vidmodeFromModeInfo(mi, ci); 505 506 XRRFreeCrtcInfo(ci); 507 } 508 509 XRRFreeScreenResources(sr); 510 } 511 else 512 { 513 mode->width = DisplayWidth(_glfw.x11.display, _glfw.x11.screen); 514 mode->height = DisplayHeight(_glfw.x11.display, _glfw.x11.screen); 515 mode->refreshRate = 0; 516 517 _glfwSplitBPP(DefaultDepth(_glfw.x11.display, _glfw.x11.screen), 518 &mode->redBits, &mode->greenBits, &mode->blueBits); 519 } 520 } 521 522 GLFWbool _glfwPlatformGetGammaRamp(_GLFWmonitor* monitor, GLFWgammaramp* ramp) 523 { 524 if (_glfw.x11.randr.available && !_glfw.x11.randr.gammaBroken) 525 { 526 const size_t size = XRRGetCrtcGammaSize(_glfw.x11.display, 527 monitor->x11.crtc); 528 XRRCrtcGamma* gamma = XRRGetCrtcGamma(_glfw.x11.display, 529 monitor->x11.crtc); 530 531 _glfwAllocGammaArrays(ramp, size); 532 533 memcpy(ramp->red, gamma->red, size * sizeof(unsigned short)); 534 memcpy(ramp->green, gamma->green, size * sizeof(unsigned short)); 535 memcpy(ramp->blue, gamma->blue, size * sizeof(unsigned short)); 536 537 XRRFreeGamma(gamma); 538 return GLFW_TRUE; 539 } 540 else if (_glfw.x11.vidmode.available) 541 { 542 int size; 543 XF86VidModeGetGammaRampSize(_glfw.x11.display, _glfw.x11.screen, &size); 544 545 _glfwAllocGammaArrays(ramp, size); 546 547 XF86VidModeGetGammaRamp(_glfw.x11.display, 548 _glfw.x11.screen, 549 ramp->size, ramp->red, ramp->green, ramp->blue); 550 return GLFW_TRUE; 551 } 552 else 553 { 554 _glfwInputError(GLFW_PLATFORM_ERROR, 555 "X11: Gamma ramp access not supported by server"); 556 return GLFW_FALSE; 557 } 558 } 559 560 void _glfwPlatformSetGammaRamp(_GLFWmonitor* monitor, const GLFWgammaramp* ramp) 561 { 562 if (_glfw.x11.randr.available && !_glfw.x11.randr.gammaBroken) 563 { 564 if (XRRGetCrtcGammaSize(_glfw.x11.display, monitor->x11.crtc) != ramp->size) 565 { 566 _glfwInputError(GLFW_PLATFORM_ERROR, 567 "X11: Gamma ramp size must match current ramp size"); 568 return; 569 } 570 571 XRRCrtcGamma* gamma = XRRAllocGamma(ramp->size); 572 573 memcpy(gamma->red, ramp->red, ramp->size * sizeof(unsigned short)); 574 memcpy(gamma->green, ramp->green, ramp->size * sizeof(unsigned short)); 575 memcpy(gamma->blue, ramp->blue, ramp->size * sizeof(unsigned short)); 576 577 XRRSetCrtcGamma(_glfw.x11.display, monitor->x11.crtc, gamma); 578 XRRFreeGamma(gamma); 579 } 580 else if (_glfw.x11.vidmode.available) 581 { 582 XF86VidModeSetGammaRamp(_glfw.x11.display, 583 _glfw.x11.screen, 584 ramp->size, 585 (unsigned short*) ramp->red, 586 (unsigned short*) ramp->green, 587 (unsigned short*) ramp->blue); 588 } 589 else 590 { 591 _glfwInputError(GLFW_PLATFORM_ERROR, 592 "X11: Gamma ramp access not supported by server"); 593 } 594 } 595 596 597 ////////////////////////////////////////////////////////////////////////// 598 ////// GLFW native API ////// 599 ////////////////////////////////////////////////////////////////////////// 600 601 GLFWAPI RRCrtc glfwGetX11Adapter(GLFWmonitor* handle) 602 { 603 _GLFWmonitor* monitor = (_GLFWmonitor*) handle; 604 _GLFW_REQUIRE_INIT_OR_RETURN(None); 605 return monitor->x11.crtc; 606 } 607 608 GLFWAPI RROutput glfwGetX11Monitor(GLFWmonitor* handle) 609 { 610 _GLFWmonitor* monitor = (_GLFWmonitor*) handle; 611 _GLFW_REQUIRE_INIT_OR_RETURN(None); 612 return monitor->x11.output; 613 } 614