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