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 // TODO this should use kMaxWorkspaces 235 else if (workspace > 31) 236 return B_BAD_INDEX; 237 238 mode = fOriginal[workspace]; 239 240 return B_OK; 241 } 242 243 244 /*! This method assumes that you already reverted to the correct number 245 of workspaces. 246 */ 247 status_t 248 ScreenMode::Revert() 249 { 250 if (!fUpdatedModes) 251 return B_ERROR; 252 253 status_t result = B_OK; 254 screen_mode current; 255 for (int32 workspace = 0; workspace < count_workspaces(); workspace++) { 256 if (Get(current, workspace) == B_OK && fOriginal[workspace] == current) 257 continue; 258 259 BScreen screen(fWindow); 260 261 // TODO: our app_server doesn't fully support workspaces, yet 262 if (workspace == current_workspace()) { 263 SetSwapDisplays(&screen, fOriginal[workspace].swap_displays); 264 SetUseLaptopPanel(&screen, fOriginal[workspace].use_laptop_panel); 265 SetTVStandard(&screen, fOriginal[workspace].tv_standard); 266 } 267 268 result = screen.SetMode(workspace, &fOriginalDisplayMode[workspace], 269 true); 270 if (result != B_OK) 271 break; 272 } 273 274 return result; 275 } 276 277 278 void 279 ScreenMode::UpdateOriginalModes() 280 { 281 BScreen screen(fWindow); 282 for (int32 workspace = 0; workspace < count_workspaces(); workspace++) { 283 if (screen.GetMode(workspace, &fOriginalDisplayMode[workspace]) 284 == B_OK) { 285 Get(fOriginal[workspace], workspace); 286 fUpdatedModes = true; 287 } 288 } 289 } 290 291 292 bool 293 ScreenMode::SupportsColorSpace(const screen_mode& mode, color_space space) 294 { 295 return true; 296 } 297 298 299 status_t 300 ScreenMode::GetRefreshLimits(const screen_mode& mode, float& min, float& max) 301 { 302 uint32 minClock, maxClock; 303 display_mode displayMode; 304 if (!_GetDisplayMode(mode, displayMode)) 305 return B_ERROR; 306 307 BScreen screen(fWindow); 308 if (screen.GetPixelClockLimits(&displayMode, &minClock, &maxClock) < B_OK) 309 return B_ERROR; 310 311 uint32 total = displayMode.timing.h_total * displayMode.timing.v_total; 312 min = minClock * 1000.0 / total; 313 max = maxClock * 1000.0 / total; 314 315 return B_OK; 316 } 317 318 319 status_t 320 ScreenMode::GetMonitorInfo(monitor_info& info, float* _diagonalInches) 321 { 322 BScreen screen(fWindow); 323 status_t status = screen.GetMonitorInfo(&info); 324 if (status != B_OK) 325 return status; 326 327 if (_diagonalInches != NULL) { 328 *_diagonalInches = round(sqrt(info.width * info.width 329 + info.height * info.height) / 0.254) / 10.0; 330 } 331 332 // Some older CRT monitors do not contain the monitor range information 333 // (EDID1_MONITOR_RANGES) in their EDID info resulting in the min/max 334 // horizontal/vertical frequencies being zero. In this case, set the 335 // vertical frequency range to 60..85 Hz. 336 if (info.min_vertical_frequency == 0) { 337 info.min_vertical_frequency = 60; 338 info.max_vertical_frequency = 85; 339 } 340 341 // TODO: If the names aren't sound, we could see if we find/create a 342 // database for the entries with user presentable names; they are fine 343 // for the models I could test with so far. 344 345 uint32 id = (info.vendor[0] << 24) | (info.vendor[1] << 16) 346 | (info.vendor[2] << 8) | (info.vendor[3]); 347 348 switch (id) { 349 case 'ADI\0': 350 strcpy(info.vendor, "ADI MicroScan"); 351 break; 352 case 'AAC\0': 353 case 'ACR\0': 354 case 'API\0': 355 strcpy(info.vendor, "Acer"); 356 break; 357 case 'ACT\0': 358 strcpy(info.vendor, "Targa"); 359 break; 360 case 'APP\0': 361 strcpy(info.vendor, "Apple"); 362 break; 363 case 'AUO\0': 364 strcpy(info.vendor, "AU Optronics"); 365 break; 366 case 'BNQ\0': 367 strcpy(info.vendor, "BenQ"); 368 break; 369 case 'CPL\0': 370 strcpy(info.vendor, "ALFA"); 371 break; 372 case 'CPQ\0': 373 strcpy(info.vendor, "Compaq"); 374 break; 375 case 'DEL\0': 376 strcpy(info.vendor, "Dell"); 377 break; 378 case 'DPC\0': 379 strcpy(info.vendor, "Delta Electronics"); 380 break; 381 case 'DWE\0': 382 strcpy(info.vendor, "Daewoo"); 383 break; 384 case 'ECS\0': 385 strcpy(info.vendor, "Elitegroup"); 386 break; 387 case 'ELS\0': 388 strcpy(info.vendor, "ELSA"); 389 break; 390 case 'EMA\0': 391 strcpy(info.vendor, "eMachines"); 392 break; 393 case 'EIZ\0': 394 case 'ENC\0': 395 strcpy(info.vendor, "Eizo"); 396 break; 397 case 'EPI\0': 398 strcpy(info.vendor, "Envision"); 399 break; 400 case 'FCM\0': 401 strcpy(info.vendor, "Funai Electronics"); 402 break; 403 case 'FUS\0': 404 strcpy(info.vendor, "Fujitsu-Siemens"); 405 break; 406 case 'GSM\0': 407 strcpy(info.vendor, "LG"); 408 break; 409 case 'GWY\0': 410 strcpy(info.vendor, "Gateway"); 411 break; 412 case 'HIQ\0': 413 case 'HEI\0': 414 strcpy(info.vendor, "Hyundai"); 415 break; 416 case 'HIT\0': 417 case 'HTC\0': 418 strcpy(info.vendor, "Hitachi"); 419 break; 420 case 'HSL\0': 421 strcpy(info.vendor, "Hansol"); 422 break; 423 case 'HWP\0': 424 strcpy(info.vendor, "Hewlett Packard"); 425 break; 426 case 'ICL\0': 427 strcpy(info.vendor, "Fujitsu"); 428 break; 429 case 'IVM\0': 430 strcpy(info.vendor, "Iiyama"); 431 break; 432 case 'LEN\0': 433 strcpy(info.vendor, "Lenovo"); 434 break; 435 case 'LPL\0': 436 strcpy(info.vendor, "LG Phillips"); 437 break; 438 case 'LTN\0': 439 strcpy(info.vendor, "Lite-On"); 440 break; 441 case 'MAX\0': 442 strcpy(info.vendor, "Maxdata"); 443 break; 444 case 'MED\0': 445 strcpy(info.vendor, "Medion"); 446 break; 447 case 'MEI\0': 448 strcpy(info.vendor, "Panasonic"); 449 break; 450 case 'MEL\0': 451 strcpy(info.vendor, "Mitsubishi"); 452 break; 453 case 'MIR\0': 454 strcpy(info.vendor, "miro"); 455 break; 456 case 'MTC\0': 457 strcpy(info.vendor, "Mitac"); 458 break; 459 case 'NAN\0': 460 strcpy(info.vendor, "Nanao"); 461 break; 462 case 'NOK\0': 463 strcpy(info.vendor, "Nokia"); 464 break; 465 case 'OQI\0': 466 strcpy(info.vendor, "Optiquest"); 467 break; 468 case 'PHL\0': 469 strcpy(info.vendor, "Philips"); 470 break; 471 case 'PTS\0': 472 strcpy(info.vendor, "Proview"); 473 break; 474 case 'QDS\0': 475 strcpy(info.vendor, "Quanta Display"); 476 break; 477 case 'REL\0': 478 strcpy(info.vendor, "Relisys"); 479 break; 480 case 'SAM\0': 481 strcpy(info.vendor, "Samsung"); 482 break; 483 case 'SDI\0': 484 strcpy(info.vendor, "Samtron"); 485 break; 486 case 'SHP\0': 487 strcpy(info.vendor, "Sharp"); 488 break; 489 case 'SNI\0': 490 strcpy(info.vendor, "Siemens"); 491 break; 492 case 'SNY\0': 493 strcpy(info.vendor, "Sony"); 494 break; 495 case 'SPT\0': 496 strcpy(info.vendor, "Sceptre"); 497 break; 498 case 'SRC\0': 499 strcpy(info.vendor, "Shamrock"); 500 break; 501 case 'SUN\0': 502 strcpy(info.vendor, "Sun Microsystems"); 503 break; 504 case 'TAT\0': 505 strcpy(info.vendor, "Tatung"); 506 break; 507 case 'TOS\0': 508 case 'TSB\0': 509 strcpy(info.vendor, "Toshiba"); 510 break; 511 case 'UNM\0': 512 strcpy(info.vendor, "Unisys"); 513 break; 514 case 'VIZ\0': 515 strcpy(info.vendor, "Vizio"); 516 break; 517 case 'VSC\0': 518 strcpy(info.vendor, "ViewSonic"); 519 break; 520 case 'ZCM\0': 521 strcpy(info.vendor, "Zenith"); 522 break; 523 } 524 525 // Remove extraneous vendor strings and whitespace 526 527 BString name(info.name); 528 name.IReplaceAll(info.vendor, ""); 529 name.Trim(); 530 531 strcpy(info.name, name.String()); 532 533 return B_OK; 534 } 535 536 537 status_t 538 ScreenMode::GetDeviceInfo(accelerant_device_info& info) 539 { 540 BScreen screen(fWindow); 541 return screen.GetDeviceInfo(&info); 542 } 543 544 545 screen_mode 546 ScreenMode::ModeAt(int32 index) 547 { 548 if (index < 0) 549 index = 0; 550 else if (index >= (int32)fModeCount) 551 index = fModeCount - 1; 552 553 screen_mode mode; 554 mode.SetTo(fModeList[index]); 555 556 return mode; 557 } 558 559 560 int32 561 ScreenMode::CountModes() 562 { 563 return fModeCount; 564 } 565 566 567 bool 568 ScreenMode::_GetDisplayMode(const screen_mode& mode, display_mode& displayMode) 569 { 570 uint16 virtualWidth, virtualHeight; 571 int32 bestIndex = -1; 572 float bestDiff = 999; 573 574 virtualWidth = mode.combine == kCombineHorizontally 575 ? mode.width * 2 : mode.width; 576 virtualHeight = mode.combine == kCombineVertically 577 ? mode.height * 2 : mode.height; 578 579 // try to find mode in list provided by driver 580 for (uint32 i = 0; i < fModeCount; i++) { 581 if (fModeList[i].virtual_width != virtualWidth 582 || fModeList[i].virtual_height != virtualHeight 583 || (color_space)fModeList[i].space != mode.space) 584 continue; 585 586 // Accept the mode if the computed refresh rate of the mode is within 587 // 0.6 percent of the refresh rate specified by the caller. Note that 588 // refresh rates computed from mode parameters is not exact; especially 589 // some of the older modes such as 640x480, 800x600, and 1024x768. 590 // The tolerance of 0.6% was obtained by examining the various possible 591 // modes. 592 593 float refreshDiff = fabs(get_refresh_rate(fModeList[i]) - mode.refresh); 594 if (refreshDiff < 0.006 * mode.refresh) { 595 // Accept this mode. 596 displayMode = fModeList[i]; 597 displayMode.h_display_start = 0; 598 displayMode.v_display_start = 0; 599 600 // Since the computed refresh rate of the selected mode might differ 601 // from selected refresh rate by a few tenths (e.g. 60.2 instead of 602 // 60.0), tweak the pixel clock so the the refresh rate of the mode 603 // matches the selected refresh rate. 604 605 displayMode.timing.pixel_clock = uint32(((displayMode.timing.h_total 606 * displayMode.timing.v_total * mode.refresh) / 1000.0) + 0.5); 607 return true; 608 } 609 610 // Mode not acceptable. 611 612 if (refreshDiff < bestDiff) { 613 bestDiff = refreshDiff; 614 bestIndex = i; 615 } 616 } 617 618 // we didn't find the exact mode, but something very similar? 619 if (bestIndex == -1) 620 return false; 621 622 displayMode = fModeList[bestIndex]; 623 displayMode.h_display_start = 0; 624 displayMode.v_display_start = 0; 625 626 // For the mode selected by the width, height, and refresh rate, compute 627 // the video timing parameters for the mode by using the VESA Generalized 628 // Timing Formula (GTF). 629 630 ComputeGTFVideoTiming(displayMode.timing.h_display, 631 displayMode.timing.v_display, mode.refresh, displayMode.timing); 632 633 return true; 634 } 635 636