1 /* 2 * Copyright 2005-2009, Haiku. 3 * Distributed under the terms of the MIT License. 4 * 5 * Authors: 6 * Axel Dörfler, axeld@pinc-software.de 7 */ 8 9 10 #include "ScreenMode.h" 11 #include "gtf.h" 12 13 #include <InterfaceDefs.h> 14 #include <String.h> 15 16 #include <stdlib.h> 17 #include <stdio.h> 18 #include <string.h> 19 20 21 /* Note, this headers defines a *private* interface to the Radeon accelerant. 22 * It's a solution that works with the current BeOS interface that Haiku 23 * adopted. 24 * However, it's not a nice and clean solution. Don't use this header in any 25 * application if you can avoid it. No other driver is using this, or should 26 * be using this. 27 * It will be replaced as soon as we introduce an updated accelerant interface 28 * which may even happen before R1 hits the streets. 29 */ 30 31 #include "multimon.h" // the usual: DANGER WILL, ROBINSON! 32 33 34 static combine_mode 35 get_combine_mode(display_mode& mode) 36 { 37 if ((mode.flags & B_SCROLL) == 0) 38 return kCombineDisable; 39 40 if (mode.virtual_width == mode.timing.h_display * 2) 41 return kCombineHorizontally; 42 43 if (mode.virtual_height == mode.timing.v_display * 2) 44 return kCombineVertically; 45 46 return kCombineDisable; 47 } 48 49 50 static float 51 get_refresh_rate(display_mode& mode) 52 { 53 // we have to be catious as refresh rate cannot be controlled directly, 54 // so it suffers under rounding errors and hardware restrictions 55 return rint(10 * float(mode.timing.pixel_clock * 1000) 56 / float(mode.timing.h_total * mode.timing.v_total)) / 10.0; 57 } 58 59 60 /*! Helper to sort modes by resolution */ 61 static int 62 compare_mode(const void* _mode1, const void* _mode2) 63 { 64 display_mode *mode1 = (display_mode *)_mode1; 65 display_mode *mode2 = (display_mode *)_mode2; 66 combine_mode combine1, combine2; 67 uint16 width1, width2, height1, height2; 68 69 combine1 = get_combine_mode(*mode1); 70 combine2 = get_combine_mode(*mode2); 71 72 width1 = mode1->virtual_width; 73 height1 = mode1->virtual_height; 74 width2 = mode2->virtual_width; 75 height2 = mode2->virtual_height; 76 77 if (combine1 == kCombineHorizontally) 78 width1 /= 2; 79 if (combine1 == kCombineVertically) 80 height1 /= 2; 81 if (combine2 == kCombineHorizontally) 82 width2 /= 2; 83 if (combine2 == kCombineVertically) 84 height2 /= 2; 85 86 if (width1 != width2) 87 return width1 - width2; 88 89 if (height1 != height2) 90 return height1 - height2; 91 92 return (int)(10 * get_refresh_rate(*mode1) 93 - 10 * get_refresh_rate(*mode2)); 94 } 95 96 97 // #pragma mark - 98 99 100 int32 101 screen_mode::BitsPerPixel() const 102 { 103 switch (space) { 104 case B_RGB32: return 32; 105 case B_RGB24: return 24; 106 case B_RGB16: return 16; 107 case B_RGB15: return 15; 108 case B_CMAP8: return 8; 109 default: return 0; 110 } 111 } 112 113 114 bool 115 screen_mode::operator==(const screen_mode &other) const 116 { 117 return !(*this != other); 118 } 119 120 121 bool 122 screen_mode::operator!=(const screen_mode &other) const 123 { 124 return width != other.width || height != other.height 125 || space != other.space || refresh != other.refresh 126 || combine != other.combine 127 || swap_displays != other.swap_displays 128 || use_laptop_panel != other.use_laptop_panel 129 || tv_standard != other.tv_standard; 130 } 131 132 133 void 134 screen_mode::SetTo(display_mode& mode) 135 { 136 width = mode.virtual_width; 137 height = mode.virtual_height; 138 space = (color_space)mode.space; 139 combine = get_combine_mode(mode); 140 refresh = get_refresh_rate(mode); 141 142 if (combine == kCombineHorizontally) 143 width /= 2; 144 else if (combine == kCombineVertically) 145 height /= 2; 146 147 swap_displays = false; 148 use_laptop_panel = false; 149 tv_standard = 0; 150 } 151 152 153 // #pragma mark - 154 155 156 ScreenMode::ScreenMode(BWindow* window) 157 : 158 fWindow(window), 159 fUpdatedModes(false) 160 { 161 BScreen screen(window); 162 if (screen.GetModeList(&fModeList, &fModeCount) == B_OK) { 163 // sort modes by resolution and refresh to make 164 // the resolution and refresh menu look nicer 165 qsort(fModeList, fModeCount, sizeof(display_mode), compare_mode); 166 } else { 167 fModeList = NULL; 168 fModeCount = 0; 169 } 170 } 171 172 173 ScreenMode::~ScreenMode() 174 { 175 free(fModeList); 176 } 177 178 179 status_t 180 ScreenMode::Set(const screen_mode& mode, int32 workspace) 181 { 182 if (!fUpdatedModes) 183 UpdateOriginalModes(); 184 185 BScreen screen(fWindow); 186 187 if (workspace == ~0) 188 workspace = current_workspace(); 189 190 // TODO: our app_server doesn't fully support workspaces, yet 191 SetSwapDisplays(&screen, mode.swap_displays); 192 SetUseLaptopPanel(&screen, mode.use_laptop_panel); 193 SetTVStandard(&screen, mode.tv_standard); 194 195 display_mode displayMode; 196 if (!_GetDisplayMode(mode, displayMode)) 197 return B_ENTRY_NOT_FOUND; 198 199 return screen.SetMode(workspace, &displayMode, true); 200 } 201 202 203 status_t 204 ScreenMode::Get(screen_mode& mode, int32 workspace) const 205 { 206 display_mode displayMode; 207 BScreen screen(fWindow); 208 209 if (workspace == ~0) 210 workspace = current_workspace(); 211 212 if (screen.GetMode(workspace, &displayMode) != B_OK) 213 return B_ERROR; 214 215 mode.SetTo(displayMode); 216 217 // TODO: our app_server doesn't fully support workspaces, yet 218 if (GetSwapDisplays(&screen, &mode.swap_displays) != B_OK) 219 mode.swap_displays = false; 220 if (GetUseLaptopPanel(&screen, &mode.use_laptop_panel) != B_OK) 221 mode.use_laptop_panel = false; 222 if (GetTVStandard(&screen, &mode.tv_standard) != B_OK) 223 mode.tv_standard = 0; 224 225 return B_OK; 226 } 227 228 229 status_t 230 ScreenMode::GetOriginalMode(screen_mode& mode, int32 workspace) const 231 { 232 if (workspace == ~0) 233 workspace = current_workspace(); 234 else if(workspace > 31) 235 return B_BAD_INDEX; 236 237 mode = fOriginal[workspace]; 238 239 return B_OK; 240 } 241 242 243 /*! This method assumes that you already reverted to the correct number 244 of workspaces. 245 */ 246 status_t 247 ScreenMode::Revert() 248 { 249 if (!fUpdatedModes) 250 return B_ERROR; 251 252 status_t result = B_OK; 253 screen_mode current; 254 for (int32 workspace = 0; workspace < count_workspaces(); workspace++) { 255 if (Get(current, workspace) == B_OK && fOriginal[workspace] == current) 256 continue; 257 258 BScreen screen(fWindow); 259 260 // TODO: our app_server doesn't fully support workspaces, yet 261 if (workspace == current_workspace()) { 262 SetSwapDisplays(&screen, fOriginal[workspace].swap_displays); 263 SetUseLaptopPanel(&screen, fOriginal[workspace].use_laptop_panel); 264 SetTVStandard(&screen, fOriginal[workspace].tv_standard); 265 } 266 267 result = screen.SetMode(workspace, &fOriginalDisplayMode[workspace], 268 true); 269 if (result != B_OK) 270 break; 271 } 272 273 return result; 274 } 275 276 277 void 278 ScreenMode::UpdateOriginalModes() 279 { 280 BScreen screen(fWindow); 281 for (int32 workspace = 0; workspace < count_workspaces(); workspace++) { 282 if (screen.GetMode(workspace, &fOriginalDisplayMode[workspace]) 283 == B_OK) { 284 Get(fOriginal[workspace], workspace); 285 fUpdatedModes = true; 286 } 287 } 288 } 289 290 291 bool 292 ScreenMode::SupportsColorSpace(const screen_mode& mode, color_space space) 293 { 294 return true; 295 } 296 297 298 status_t 299 ScreenMode::GetRefreshLimits(const screen_mode& mode, float& min, float& max) 300 { 301 uint32 minClock, maxClock; 302 display_mode displayMode; 303 if (!_GetDisplayMode(mode, displayMode)) 304 return B_ERROR; 305 306 BScreen screen(fWindow); 307 if (screen.GetPixelClockLimits(&displayMode, &minClock, &maxClock) < B_OK) 308 return B_ERROR; 309 310 uint32 total = displayMode.timing.h_total * displayMode.timing.v_total; 311 min = minClock * 1000.0 / total; 312 max = maxClock * 1000.0 / total; 313 314 return B_OK; 315 } 316 317 318 status_t 319 ScreenMode::GetMonitorInfo(monitor_info& info, float* _diagonalInches) 320 { 321 BScreen screen(fWindow); 322 status_t status = screen.GetMonitorInfo(&info); 323 if (status != B_OK) 324 return status; 325 326 if (_diagonalInches != NULL) { 327 *_diagonalInches = round(sqrt(info.width * info.width 328 + info.height * info.height) / 0.254) / 10.0; 329 } 330 331 // Some older CRT monitors do not contain the monitor range information 332 // (EDID1_MONITOR_RANGES) in their EDID info resulting in the min/max 333 // horizontal/vertical frequencies being zero. In this case, set the 334 // vertical frequency range to 60..85 Hz. 335 if (info.min_vertical_frequency == 0) { 336 info.min_vertical_frequency = 60; 337 info.max_vertical_frequency = 85; 338 } 339 340 // TODO: If the names aren't sound, we could see if we find/create a 341 // database for the entries with user presentable names; they are fine 342 // for the models I could test with so far. 343 344 uint32 id = (info.vendor[0] << 24) | (info.vendor[1] << 16) 345 | (info.vendor[2] << 8) | (info.vendor[3]); 346 347 switch (id) { 348 case 'ADI\0': 349 strcpy(info.vendor, "ADI MicroScan"); 350 break; 351 case 'AAC\0': 352 case 'ACR\0': 353 case 'API\0': 354 strcpy(info.vendor, "Acer"); 355 break; 356 case 'ACT\0': 357 strcpy(info.vendor, "Targa"); 358 break; 359 case 'APP\0': 360 strcpy(info.vendor, "Apple"); 361 break; 362 case 'AUO\0': 363 strcpy(info.vendor, "AU Optronics"); 364 break; 365 case 'BNQ\0': 366 strcpy(info.vendor, "BenQ"); 367 break; 368 case 'CPL\0': 369 strcpy(info.vendor, "ALFA"); 370 break; 371 case 'CPQ\0': 372 strcpy(info.vendor, "Compaq"); 373 break; 374 case 'DEL\0': 375 strcpy(info.vendor, "Dell"); 376 break; 377 case 'DPC\0': 378 strcpy(info.vendor, "Delta Electronics"); 379 break; 380 case 'DWE\0': 381 strcpy(info.vendor, "Daewoo"); 382 break; 383 case 'ECS\0': 384 strcpy(info.vendor, "Elitegroup"); 385 break; 386 case 'ELS\0': 387 strcpy(info.vendor, "ELSA"); 388 break; 389 case 'EMA\0': 390 strcpy(info.vendor, "eMachines"); 391 break; 392 case 'EIZ\0': 393 case 'ENC\0': 394 strcpy(info.vendor, "Eizo"); 395 break; 396 case 'EPI\0': 397 strcpy(info.vendor, "Envision"); 398 break; 399 case 'FCM\0': 400 strcpy(info.vendor, "Funai Electronics"); 401 break; 402 case 'FUS\0': 403 strcpy(info.vendor, "Fujitsu-Siemens"); 404 break; 405 case 'GSM\0': 406 strcpy(info.vendor, "LG"); 407 break; 408 case 'GWY\0': 409 strcpy(info.vendor, "Gateway"); 410 break; 411 case 'HIQ\0': 412 case 'HEI\0': 413 strcpy(info.vendor, "Hyundai"); 414 break; 415 case 'HIT\0': 416 case 'HTC\0': 417 strcpy(info.vendor, "Hitachi"); 418 break; 419 case 'HSL\0': 420 strcpy(info.vendor, "Hansol"); 421 break; 422 case 'HWP\0': 423 strcpy(info.vendor, "Hewlett Packard"); 424 break; 425 case 'ICL\0': 426 strcpy(info.vendor, "Fujitsu"); 427 break; 428 case 'IVM\0': 429 strcpy(info.vendor, "Iiyama"); 430 break; 431 case 'LEN\0': 432 strcpy(info.vendor, "Lenovo"); 433 break; 434 case 'LPL\0': 435 strcpy(info.vendor, "LG Phillips"); 436 break; 437 case 'LTN\0': 438 strcpy(info.vendor, "Lite-On"); 439 break; 440 case 'MAX\0': 441 strcpy(info.vendor, "Maxdata"); 442 break; 443 case 'MED\0': 444 strcpy(info.vendor, "Medion"); 445 break; 446 case 'MEI\0': 447 strcpy(info.vendor, "Panasonic"); 448 break; 449 case 'MEL\0': 450 strcpy(info.vendor, "Mitsubishi"); 451 break; 452 case 'MIR\0': 453 strcpy(info.vendor, "miro"); 454 break; 455 case 'MTC\0': 456 strcpy(info.vendor, "Mitac"); 457 break; 458 case 'NAN\0': 459 strcpy(info.vendor, "Nanao"); 460 break; 461 case 'NOK\0': 462 strcpy(info.vendor, "Nokia"); 463 break; 464 case 'OQI\0': 465 strcpy(info.vendor, "Optiquest"); 466 break; 467 case 'PHL\0': 468 strcpy(info.vendor, "Philips"); 469 break; 470 case 'PTS\0': 471 strcpy(info.vendor, "Proview"); 472 break; 473 case 'QDS\0': 474 strcpy(info.vendor, "Quanta Display"); 475 break; 476 case 'REL\0': 477 strcpy(info.vendor, "Relisys"); 478 break; 479 case 'SAM\0': 480 strcpy(info.vendor, "Samsung"); 481 break; 482 case 'SDI\0': 483 strcpy(info.vendor, "Samtron"); 484 break; 485 case 'SHP\0': 486 strcpy(info.vendor, "Sharp"); 487 break; 488 case 'SNI\0': 489 strcpy(info.vendor, "Siemens"); 490 break; 491 case 'SNY\0': 492 strcpy(info.vendor, "Sony"); 493 break; 494 case 'SPT\0': 495 strcpy(info.vendor, "Sceptre"); 496 break; 497 case 'SRC\0': 498 strcpy(info.vendor, "Shamrock"); 499 break; 500 case 'SUN\0': 501 strcpy(info.vendor, "Sun Microsystems"); 502 break; 503 case 'TAT\0': 504 strcpy(info.vendor, "Tatung"); 505 break; 506 case 'TOS\0': 507 case 'TSB\0': 508 strcpy(info.vendor, "Toshiba"); 509 break; 510 case 'UNM\0': 511 strcpy(info.vendor, "Unisys"); 512 break; 513 case 'VIZ\0': 514 strcpy(info.vendor, "Vizio"); 515 break; 516 case 'VSC\0': 517 strcpy(info.vendor, "ViewSonic"); 518 break; 519 case 'ZCM\0': 520 strcpy(info.vendor, "Zenith"); 521 break; 522 } 523 524 // Remove extraneous vendor strings and whitespace 525 526 BString name(info.name); 527 name.IReplaceAll(info.vendor, ""); 528 name.Trim(); 529 530 strcpy(info.name, name.String()); 531 532 return B_OK; 533 } 534 535 536 status_t 537 ScreenMode::GetDeviceInfo(accelerant_device_info& info) 538 { 539 BScreen screen(fWindow); 540 return screen.GetDeviceInfo(&info); 541 } 542 543 544 screen_mode 545 ScreenMode::ModeAt(int32 index) 546 { 547 if (index < 0) 548 index = 0; 549 else if (index >= (int32)fModeCount) 550 index = fModeCount - 1; 551 552 screen_mode mode; 553 mode.SetTo(fModeList[index]); 554 555 return mode; 556 } 557 558 559 int32 560 ScreenMode::CountModes() 561 { 562 return fModeCount; 563 } 564 565 566 bool 567 ScreenMode::_GetDisplayMode(const screen_mode& mode, display_mode& displayMode) 568 { 569 uint16 virtualWidth, virtualHeight; 570 int32 bestIndex = -1; 571 float bestDiff = 999; 572 573 virtualWidth = mode.combine == kCombineHorizontally 574 ? mode.width * 2 : mode.width; 575 virtualHeight = mode.combine == kCombineVertically 576 ? mode.height * 2 : mode.height; 577 578 // try to find mode in list provided by driver 579 for (uint32 i = 0; i < fModeCount; i++) { 580 if (fModeList[i].virtual_width != virtualWidth 581 || fModeList[i].virtual_height != virtualHeight 582 || (color_space)fModeList[i].space != mode.space) 583 continue; 584 585 // Accept the mode if the computed refresh rate of the mode is within 586 // 0.6 percent of the refresh rate specified by the caller. Note that 587 // refresh rates computed from mode parameters is not exact; especially 588 // some of the older modes such as 640x480, 800x600, and 1024x768. 589 // The tolerance of 0.6% was obtained by examining the various possible 590 // modes. 591 592 float refreshDiff = fabs(get_refresh_rate(fModeList[i]) - mode.refresh); 593 if (refreshDiff < 0.006 * mode.refresh) { 594 // Accept this mode. 595 displayMode = fModeList[i]; 596 displayMode.h_display_start = 0; 597 displayMode.v_display_start = 0; 598 599 // Since the computed refresh rate of the selected mode might differ 600 // from selected refresh rate by a few tenths (e.g. 60.2 instead of 601 // 60.0), tweak the pixel clock so the the refresh rate of the mode 602 // matches the selected refresh rate. 603 604 displayMode.timing.pixel_clock = uint32(((displayMode.timing.h_total 605 * displayMode.timing.v_total * mode.refresh) / 1000.0) + 0.5); 606 return true; 607 } 608 609 // Mode not acceptable. 610 611 if (refreshDiff < bestDiff) { 612 bestDiff = refreshDiff; 613 bestIndex = i; 614 } 615 } 616 617 // we didn't find the exact mode, but something very similar? 618 if (bestIndex == -1) 619 return false; 620 621 displayMode = fModeList[bestIndex]; 622 displayMode.h_display_start = 0; 623 displayMode.v_display_start = 0; 624 625 // For the mode selected by the width, height, and refresh rate, compute 626 // the video timing parameters for the mode by using the VESA Generalized 627 // Timing Formula (GTF). 628 629 ComputeGTFVideoTiming(displayMode.timing.h_display, 630 displayMode.timing.v_display, mode.refresh, displayMode.timing); 631 632 return true; 633 } 634 635