monitor.c (15194B)
1 //======================================================================== 2 // GLFW 3.4 - 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 #include <assert.h> 31 #include <math.h> 32 #include <float.h> 33 #include <string.h> 34 #include <stdlib.h> 35 #include <limits.h> 36 37 38 // Lexically compare video modes, used by qsort 39 // 40 static int compareVideoModes(const void* fp, const void* sp) 41 { 42 const GLFWvidmode* fm = fp; 43 const GLFWvidmode* sm = sp; 44 const int fbpp = fm->redBits + fm->greenBits + fm->blueBits; 45 const int sbpp = sm->redBits + sm->greenBits + sm->blueBits; 46 const int farea = fm->width * fm->height; 47 const int sarea = sm->width * sm->height; 48 49 // First sort on color bits per pixel 50 if (fbpp != sbpp) 51 return fbpp - sbpp; 52 53 // Then sort on screen area 54 if (farea != sarea) 55 return farea - sarea; 56 57 // Then sort on width 58 if (fm->width != sm->width) 59 return fm->width - sm->width; 60 61 // Lastly sort on refresh rate 62 return fm->refreshRate - sm->refreshRate; 63 } 64 65 // Retrieves the available modes for the specified monitor 66 // 67 static GLFWbool refreshVideoModes(_GLFWmonitor* monitor) 68 { 69 int modeCount; 70 GLFWvidmode* modes; 71 72 if (monitor->modes) 73 return GLFW_TRUE; 74 75 modes = _glfw.platform.getVideoModes(monitor, &modeCount); 76 if (!modes) 77 return GLFW_FALSE; 78 79 qsort(modes, modeCount, sizeof(GLFWvidmode), compareVideoModes); 80 81 _glfw_free(monitor->modes); 82 monitor->modes = modes; 83 monitor->modeCount = modeCount; 84 85 return GLFW_TRUE; 86 } 87 88 89 ////////////////////////////////////////////////////////////////////////// 90 ////// GLFW event API ////// 91 ////////////////////////////////////////////////////////////////////////// 92 93 // Notifies shared code of a monitor connection or disconnection 94 // 95 void _glfwInputMonitor(_GLFWmonitor* monitor, int action, int placement) 96 { 97 assert(monitor != NULL); 98 assert(action == GLFW_CONNECTED || action == GLFW_DISCONNECTED); 99 assert(placement == _GLFW_INSERT_FIRST || placement == _GLFW_INSERT_LAST); 100 101 if (action == GLFW_CONNECTED) 102 { 103 _glfw.monitorCount++; 104 _glfw.monitors = 105 _glfw_realloc(_glfw.monitors, 106 sizeof(_GLFWmonitor*) * _glfw.monitorCount); 107 108 if (placement == _GLFW_INSERT_FIRST) 109 { 110 memmove(_glfw.monitors + 1, 111 _glfw.monitors, 112 ((size_t) _glfw.monitorCount - 1) * sizeof(_GLFWmonitor*)); 113 _glfw.monitors[0] = monitor; 114 } 115 else 116 _glfw.monitors[_glfw.monitorCount - 1] = monitor; 117 } 118 else if (action == GLFW_DISCONNECTED) 119 { 120 int i; 121 _GLFWwindow* window; 122 123 for (window = _glfw.windowListHead; window; window = window->next) 124 { 125 if (window->monitor == monitor) 126 { 127 int width, height, xoff, yoff; 128 _glfw.platform.getWindowSize(window, &width, &height); 129 _glfw.platform.setWindowMonitor(window, NULL, 0, 0, width, height, 0); 130 _glfw.platform.getWindowFrameSize(window, &xoff, &yoff, NULL, NULL); 131 _glfw.platform.setWindowPos(window, xoff, yoff); 132 } 133 } 134 135 for (i = 0; i < _glfw.monitorCount; i++) 136 { 137 if (_glfw.monitors[i] == monitor) 138 { 139 _glfw.monitorCount--; 140 memmove(_glfw.monitors + i, 141 _glfw.monitors + i + 1, 142 ((size_t) _glfw.monitorCount - i) * sizeof(_GLFWmonitor*)); 143 break; 144 } 145 } 146 } 147 148 if (_glfw.callbacks.monitor) 149 _glfw.callbacks.monitor((GLFWmonitor*) monitor, action); 150 151 if (action == GLFW_DISCONNECTED) 152 _glfwFreeMonitor(monitor); 153 } 154 155 // Notifies shared code that a full screen window has acquired or released 156 // a monitor 157 // 158 void _glfwInputMonitorWindow(_GLFWmonitor* monitor, _GLFWwindow* window) 159 { 160 assert(monitor != NULL); 161 monitor->window = window; 162 } 163 164 165 ////////////////////////////////////////////////////////////////////////// 166 ////// GLFW internal API ////// 167 ////////////////////////////////////////////////////////////////////////// 168 169 // Allocates and returns a monitor object with the specified name and dimensions 170 // 171 _GLFWmonitor* _glfwAllocMonitor(const char* name, int widthMM, int heightMM) 172 { 173 _GLFWmonitor* monitor = _glfw_calloc(1, sizeof(_GLFWmonitor)); 174 monitor->widthMM = widthMM; 175 monitor->heightMM = heightMM; 176 177 strncpy(monitor->name, name, sizeof(monitor->name) - 1); 178 179 return monitor; 180 } 181 182 // Frees a monitor object and any data associated with it 183 // 184 void _glfwFreeMonitor(_GLFWmonitor* monitor) 185 { 186 if (monitor == NULL) 187 return; 188 189 _glfw.platform.freeMonitor(monitor); 190 191 _glfwFreeGammaArrays(&monitor->originalRamp); 192 _glfwFreeGammaArrays(&monitor->currentRamp); 193 194 _glfw_free(monitor->modes); 195 _glfw_free(monitor); 196 } 197 198 // Allocates red, green and blue value arrays of the specified size 199 // 200 void _glfwAllocGammaArrays(GLFWgammaramp* ramp, unsigned int size) 201 { 202 ramp->red = _glfw_calloc(size, sizeof(unsigned short)); 203 ramp->green = _glfw_calloc(size, sizeof(unsigned short)); 204 ramp->blue = _glfw_calloc(size, sizeof(unsigned short)); 205 ramp->size = size; 206 } 207 208 // Frees the red, green and blue value arrays and clears the struct 209 // 210 void _glfwFreeGammaArrays(GLFWgammaramp* ramp) 211 { 212 _glfw_free(ramp->red); 213 _glfw_free(ramp->green); 214 _glfw_free(ramp->blue); 215 216 memset(ramp, 0, sizeof(GLFWgammaramp)); 217 } 218 219 // Chooses the video mode most closely matching the desired one 220 // 221 const GLFWvidmode* _glfwChooseVideoMode(_GLFWmonitor* monitor, 222 const GLFWvidmode* desired) 223 { 224 int i; 225 unsigned int sizeDiff, leastSizeDiff = UINT_MAX; 226 unsigned int rateDiff, leastRateDiff = UINT_MAX; 227 unsigned int colorDiff, leastColorDiff = UINT_MAX; 228 const GLFWvidmode* current; 229 const GLFWvidmode* closest = NULL; 230 231 if (!refreshVideoModes(monitor)) 232 return NULL; 233 234 for (i = 0; i < monitor->modeCount; i++) 235 { 236 current = monitor->modes + i; 237 238 colorDiff = 0; 239 240 if (desired->redBits != GLFW_DONT_CARE) 241 colorDiff += abs(current->redBits - desired->redBits); 242 if (desired->greenBits != GLFW_DONT_CARE) 243 colorDiff += abs(current->greenBits - desired->greenBits); 244 if (desired->blueBits != GLFW_DONT_CARE) 245 colorDiff += abs(current->blueBits - desired->blueBits); 246 247 sizeDiff = abs((current->width - desired->width) * 248 (current->width - desired->width) + 249 (current->height - desired->height) * 250 (current->height - desired->height)); 251 252 if (desired->refreshRate != GLFW_DONT_CARE) 253 rateDiff = abs(current->refreshRate - desired->refreshRate); 254 else 255 rateDiff = UINT_MAX - current->refreshRate; 256 257 if ((colorDiff < leastColorDiff) || 258 (colorDiff == leastColorDiff && sizeDiff < leastSizeDiff) || 259 (colorDiff == leastColorDiff && sizeDiff == leastSizeDiff && rateDiff < leastRateDiff)) 260 { 261 closest = current; 262 leastSizeDiff = sizeDiff; 263 leastRateDiff = rateDiff; 264 leastColorDiff = colorDiff; 265 } 266 } 267 268 return closest; 269 } 270 271 // Performs lexical comparison between two @ref GLFWvidmode structures 272 // 273 int _glfwCompareVideoModes(const GLFWvidmode* fm, const GLFWvidmode* sm) 274 { 275 return compareVideoModes(fm, sm); 276 } 277 278 // Splits a color depth into red, green and blue bit depths 279 // 280 void _glfwSplitBPP(int bpp, int* red, int* green, int* blue) 281 { 282 int delta; 283 284 // We assume that by 32 the user really meant 24 285 if (bpp == 32) 286 bpp = 24; 287 288 // Convert "bits per pixel" to red, green & blue sizes 289 290 *red = *green = *blue = bpp / 3; 291 delta = bpp - (*red * 3); 292 if (delta >= 1) 293 *green = *green + 1; 294 295 if (delta == 2) 296 *red = *red + 1; 297 } 298 299 300 ////////////////////////////////////////////////////////////////////////// 301 ////// GLFW public API ////// 302 ////////////////////////////////////////////////////////////////////////// 303 304 GLFWAPI GLFWmonitor** glfwGetMonitors(int* count) 305 { 306 assert(count != NULL); 307 308 *count = 0; 309 310 _GLFW_REQUIRE_INIT_OR_RETURN(NULL); 311 312 *count = _glfw.monitorCount; 313 return (GLFWmonitor**) _glfw.monitors; 314 } 315 316 GLFWAPI GLFWmonitor* glfwGetPrimaryMonitor(void) 317 { 318 _GLFW_REQUIRE_INIT_OR_RETURN(NULL); 319 320 if (!_glfw.monitorCount) 321 return NULL; 322 323 return (GLFWmonitor*) _glfw.monitors[0]; 324 } 325 326 GLFWAPI void glfwGetMonitorPos(GLFWmonitor* handle, int* xpos, int* ypos) 327 { 328 _GLFWmonitor* monitor = (_GLFWmonitor*) handle; 329 assert(monitor != NULL); 330 331 if (xpos) 332 *xpos = 0; 333 if (ypos) 334 *ypos = 0; 335 336 _GLFW_REQUIRE_INIT(); 337 338 _glfw.platform.getMonitorPos(monitor, xpos, ypos); 339 } 340 341 GLFWAPI void glfwGetMonitorWorkarea(GLFWmonitor* handle, 342 int* xpos, int* ypos, 343 int* width, int* height) 344 { 345 _GLFWmonitor* monitor = (_GLFWmonitor*) handle; 346 assert(monitor != NULL); 347 348 if (xpos) 349 *xpos = 0; 350 if (ypos) 351 *ypos = 0; 352 if (width) 353 *width = 0; 354 if (height) 355 *height = 0; 356 357 _GLFW_REQUIRE_INIT(); 358 359 _glfw.platform.getMonitorWorkarea(monitor, xpos, ypos, width, height); 360 } 361 362 GLFWAPI void glfwGetMonitorPhysicalSize(GLFWmonitor* handle, int* widthMM, int* heightMM) 363 { 364 _GLFWmonitor* monitor = (_GLFWmonitor*) handle; 365 assert(monitor != NULL); 366 367 if (widthMM) 368 *widthMM = 0; 369 if (heightMM) 370 *heightMM = 0; 371 372 _GLFW_REQUIRE_INIT(); 373 374 if (widthMM) 375 *widthMM = monitor->widthMM; 376 if (heightMM) 377 *heightMM = monitor->heightMM; 378 } 379 380 GLFWAPI void glfwGetMonitorContentScale(GLFWmonitor* handle, 381 float* xscale, float* yscale) 382 { 383 _GLFWmonitor* monitor = (_GLFWmonitor*) handle; 384 assert(monitor != NULL); 385 386 if (xscale) 387 *xscale = 0.f; 388 if (yscale) 389 *yscale = 0.f; 390 391 _GLFW_REQUIRE_INIT(); 392 _glfw.platform.getMonitorContentScale(monitor, xscale, yscale); 393 } 394 395 GLFWAPI const char* glfwGetMonitorName(GLFWmonitor* handle) 396 { 397 _GLFWmonitor* monitor = (_GLFWmonitor*) handle; 398 assert(monitor != NULL); 399 400 _GLFW_REQUIRE_INIT_OR_RETURN(NULL); 401 return monitor->name; 402 } 403 404 GLFWAPI void glfwSetMonitorUserPointer(GLFWmonitor* handle, void* pointer) 405 { 406 _GLFWmonitor* monitor = (_GLFWmonitor*) handle; 407 assert(monitor != NULL); 408 409 _GLFW_REQUIRE_INIT(); 410 monitor->userPointer = pointer; 411 } 412 413 GLFWAPI void* glfwGetMonitorUserPointer(GLFWmonitor* handle) 414 { 415 _GLFWmonitor* monitor = (_GLFWmonitor*) handle; 416 assert(monitor != NULL); 417 418 _GLFW_REQUIRE_INIT_OR_RETURN(NULL); 419 return monitor->userPointer; 420 } 421 422 GLFWAPI GLFWmonitorfun glfwSetMonitorCallback(GLFWmonitorfun cbfun) 423 { 424 _GLFW_REQUIRE_INIT_OR_RETURN(NULL); 425 _GLFW_SWAP(GLFWmonitorfun, _glfw.callbacks.monitor, cbfun); 426 return cbfun; 427 } 428 429 GLFWAPI const GLFWvidmode* glfwGetVideoModes(GLFWmonitor* handle, int* count) 430 { 431 _GLFWmonitor* monitor = (_GLFWmonitor*) handle; 432 assert(monitor != NULL); 433 assert(count != NULL); 434 435 *count = 0; 436 437 _GLFW_REQUIRE_INIT_OR_RETURN(NULL); 438 439 if (!refreshVideoModes(monitor)) 440 return NULL; 441 442 *count = monitor->modeCount; 443 return monitor->modes; 444 } 445 446 GLFWAPI const GLFWvidmode* glfwGetVideoMode(GLFWmonitor* handle) 447 { 448 _GLFWmonitor* monitor = (_GLFWmonitor*) handle; 449 assert(monitor != NULL); 450 451 _GLFW_REQUIRE_INIT_OR_RETURN(NULL); 452 453 if (!_glfw.platform.getVideoMode(monitor, &monitor->currentMode)) 454 return NULL; 455 456 return &monitor->currentMode; 457 } 458 459 GLFWAPI void glfwSetGamma(GLFWmonitor* handle, float gamma) 460 { 461 unsigned int i; 462 unsigned short* values; 463 GLFWgammaramp ramp; 464 const GLFWgammaramp* original; 465 assert(handle != NULL); 466 assert(gamma > 0.f); 467 assert(gamma <= FLT_MAX); 468 469 _GLFW_REQUIRE_INIT(); 470 471 if (gamma != gamma || gamma <= 0.f || gamma > FLT_MAX) 472 { 473 _glfwInputError(GLFW_INVALID_VALUE, "Invalid gamma value %f", gamma); 474 return; 475 } 476 477 original = glfwGetGammaRamp(handle); 478 if (!original) 479 return; 480 481 values = _glfw_calloc(original->size, sizeof(unsigned short)); 482 483 for (i = 0; i < original->size; i++) 484 { 485 float value; 486 487 // Calculate intensity 488 value = i / (float) (original->size - 1); 489 // Apply gamma curve 490 value = powf(value, 1.f / gamma) * 65535.f + 0.5f; 491 // Clamp to value range 492 value = fminf(value, 65535.f); 493 494 values[i] = (unsigned short) value; 495 } 496 497 ramp.red = values; 498 ramp.green = values; 499 ramp.blue = values; 500 ramp.size = original->size; 501 502 glfwSetGammaRamp(handle, &ramp); 503 _glfw_free(values); 504 } 505 506 GLFWAPI const GLFWgammaramp* glfwGetGammaRamp(GLFWmonitor* handle) 507 { 508 _GLFWmonitor* monitor = (_GLFWmonitor*) handle; 509 assert(monitor != NULL); 510 511 _GLFW_REQUIRE_INIT_OR_RETURN(NULL); 512 513 _glfwFreeGammaArrays(&monitor->currentRamp); 514 if (!_glfw.platform.getGammaRamp(monitor, &monitor->currentRamp)) 515 return NULL; 516 517 return &monitor->currentRamp; 518 } 519 520 GLFWAPI void glfwSetGammaRamp(GLFWmonitor* handle, const GLFWgammaramp* ramp) 521 { 522 _GLFWmonitor* monitor = (_GLFWmonitor*) handle; 523 assert(monitor != NULL); 524 assert(ramp != NULL); 525 assert(ramp->size > 0); 526 assert(ramp->red != NULL); 527 assert(ramp->green != NULL); 528 assert(ramp->blue != NULL); 529 530 _GLFW_REQUIRE_INIT(); 531 532 if (ramp->size <= 0) 533 { 534 _glfwInputError(GLFW_INVALID_VALUE, 535 "Invalid gamma ramp size %i", 536 ramp->size); 537 return; 538 } 539 540 if (!monitor->originalRamp.size) 541 { 542 if (!_glfw.platform.getGammaRamp(monitor, &monitor->originalRamp)) 543 return; 544 } 545 546 _glfw.platform.setGammaRamp(monitor, ramp); 547 } 548