1 /* 2 * Copyright 2001-2005, Haiku. 3 * Distributed under the terms of the MIT License. 4 * 5 * Authors: 6 * Rafael Romo 7 * Stefano Ceccherini (burton666@libero.it) 8 * Andrew Bachmann 9 * Thomas Kurschel 10 * Axel Dörfler, axeld@pinc-software.de 11 */ 12 13 14 #include <Alert.h> 15 #include <Application.h> 16 #include <Box.h> 17 #include <Button.h> 18 #include <InterfaceDefs.h> 19 #include <MenuItem.h> 20 #include <MenuField.h> 21 #include <Messenger.h> 22 #include <PopUpMenu.h> 23 #include <Screen.h> 24 #include <String.h> 25 #include <Window.h> 26 27 #include <cstdio> 28 #include <cstdlib> 29 #include <cstring> 30 31 #include "AlertWindow.h" 32 #include "Constants.h" 33 #include "RefreshWindow.h" 34 #include "MonitorView.h" 35 #include "ScreenSettings.h" 36 #include "ScreenWindow.h" 37 #include "Utility.h" 38 39 /* Note, this headers defines a *private* interface to the Radeon accelerant. 40 * It's a solution that works with the current BeOS interface that Haiku 41 * adopted. 42 * However, it's not a nice and clean solution. Don't use this header in any 43 * application if you can avoid it. No other driver is using this, or should 44 * be using this. 45 * It will be replaced as soon as we introduce an updated accelerant interface 46 * which may even happen before R1 hits the streets. 47 */ 48 49 #include "multimon.h" // the usual: DANGER WILL, ROBINSON! 50 51 52 #define USE_FIXED_REFRESH 53 // define to use fixed standard refresh rates 54 // undefine to get standard refresh rates from driver 55 56 57 // list of officially supported colour spaces 58 static const struct { 59 color_space space; 60 int32 bits_per_pixel; 61 const char* label; 62 } kColorSpaces[] = { 63 { B_CMAP8, 8, "8 Bits/Pixel, 256 Colors" }, 64 { B_RGB15, 15, "15 Bits/Pixel, 32768 Colors" }, 65 { B_RGB16, 16, "16 Bits/Pixel, 65536 Colors" }, 66 { B_RGB32, 32, "32 Bits/Pixel, 16 Million Colors" } 67 }; 68 static const int32 kColorSpaceCount = sizeof(kColorSpaces) / sizeof(kColorSpaces[0]); 69 70 // list of standard refresh rates 71 static const int32 kRefreshRates[] = {56, 60, 70, 72, 75}; 72 static const int32 kRefreshRateCount = sizeof(kRefreshRates) / sizeof(kRefreshRates[0]); 73 74 75 // list of combine modes 76 static const struct { 77 combine_mode mode; 78 const char *name; 79 } kCombineModes[] = { 80 { kCombineDisable, "disable" }, 81 { kCombineHorizontally, "horizontally" }, 82 { kCombineVertically, "vertically" } 83 }; 84 static const int32 kCombineModeCount = sizeof(kCombineModes) / sizeof(kCombineModes[0]); 85 86 87 static BString 88 tv_standard_to_string(uint32 mode) 89 { 90 switch (mode) { 91 case 0: return "disabled"; 92 case 1: return "NTSC"; 93 case 2: return "NTSC Japan"; 94 case 3: return "PAL BDGHI"; 95 case 4: return "PAL M"; 96 case 5: return "PAL N"; 97 case 6: return "SECAM"; 98 case 101: return "NTSC 443"; 99 case 102: return "PAL 60"; 100 case 103: return "PAL NC"; 101 default: 102 { 103 BString name; 104 name << "??? (" << mode << ")"; 105 106 return name; 107 } 108 } 109 } 110 111 112 static void 113 resolution_to_string(screen_mode& mode, BString &string) 114 { 115 string << mode.width << " x " << mode.height; 116 } 117 118 119 static void 120 refresh_rate_to_string(float refresh, BString &string, 121 bool appendUnit = true, bool alwaysWithFraction = false) 122 { 123 snprintf(string.LockBuffer(32), 32, "%.*g", refresh >= 100.0 ? 4 : 3, refresh); 124 string.UnlockBuffer(); 125 126 if (appendUnit) 127 string << " Hz"; 128 } 129 130 131 static const char* 132 screen_errors(status_t status) 133 { 134 switch (status) { 135 case B_ENTRY_NOT_FOUND: 136 return "Unknown Mode"; 137 // TODO: add more? 138 139 default: 140 return strerror(status); 141 } 142 } 143 144 145 // #pragma mark - 146 147 148 ScreenWindow::ScreenWindow(ScreenSettings *Settings) 149 : BWindow(Settings->WindowFrame(), "Screen", B_TITLED_WINDOW, 150 B_NOT_RESIZABLE | B_NOT_ZOOMABLE, B_ALL_WORKSPACES), 151 fScreenMode(this), 152 fChangingAllWorkspaces(false) 153 { 154 BScreen screen(this); 155 BRect frame(Bounds()); 156 157 fScreenMode.Get(fOriginal); 158 fActive = fSelected = fOriginal; 159 160 BView *view = new BView(frame, "ScreenView", B_FOLLOW_ALL, B_WILL_DRAW); 161 view->SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR)); 162 AddChild(view); 163 164 fSettings = Settings; 165 166 // box on the left with workspace count and monitor view 167 168 BRect screenBoxRect(11.0, 18.0, 153.0, 155.0); 169 BBox *screenBox = new BBox(screenBoxRect, "left box"); 170 171 fMonitorView = new MonitorView(BRect(20.0, 16.0, 122.0, 93.0), "monitor", 172 screen.Frame().Width() + 1, screen.Frame().Height() + 1); 173 screenBox->AddChild(fMonitorView); 174 175 BPopUpMenu *popUpMenu = new BPopUpMenu("", true, true); 176 BMenuField *menuField = new BMenuField(BRect(7.0, 107.0, 135.0, 127.0), 177 "WorkspaceCountMenu", "Workspace count:", popUpMenu, true); 178 screenBox->AddChild(menuField); 179 180 for (int32 count = 1; count <= 32; count++) { 181 BString workspaceCount; 182 workspaceCount << count; 183 184 BMessage *message = new BMessage(POP_WORKSPACE_CHANGED_MSG); 185 message->AddInt32("workspace count", count); 186 187 popUpMenu->AddItem(new BMenuItem(workspaceCount.String(), 188 message)); 189 } 190 191 BMenuItem *item = popUpMenu->ItemAt(count_workspaces() - 1); 192 if (item != NULL) 193 item->SetMarked(true); 194 195 menuField->SetDivider(91.0); 196 view->AddChild(screenBox); 197 198 // box on the right with screen resolution, etc. 199 200 popUpMenu = new BPopUpMenu("Current Workspace", true, true); 201 fAllWorkspacesItem = new BMenuItem("All Workspaces", new BMessage(WORKSPACE_CHECK_MSG)); 202 popUpMenu->AddItem(fAllWorkspacesItem); 203 item = new BMenuItem("Current Workspace", new BMessage(WORKSPACE_CHECK_MSG)); 204 item->SetMarked(true); 205 popUpMenu->AddItem(item); 206 207 BRect rect(0.0, 0.0, 132.0, 18.0); 208 menuField = new BMenuField(rect, "WorkspaceMenu", NULL, popUpMenu, true); 209 210 rect.Set(164.0, 7.0, 345.0, 155.0); 211 BBox* controlsBox = new BBox(rect); 212 controlsBox->SetLabel(menuField); 213 214 rect.Set(88.0, 114.0, 200.0, 150.0); 215 fApplyButton = new BButton(rect, "ApplyButton", "Apply", 216 new BMessage(BUTTON_APPLY_MSG)); 217 fApplyButton->ResizeToPreferred(); 218 fApplyButton->SetEnabled(false); 219 220 controlsBox->AddChild(fApplyButton); 221 222 fResolutionMenu = new BPopUpMenu("resolution", true, true); 223 224 uint16 previousWidth = 0, previousHeight = 0; 225 for (int32 i = 0; i < fScreenMode.CountModes(); i++) { 226 screen_mode mode = fScreenMode.ModeAt(i); 227 228 if (mode.width == previousWidth && mode.height == previousHeight) 229 continue; 230 231 previousWidth = mode.width; 232 previousHeight = mode.height; 233 234 BMessage *message = new BMessage(POP_RESOLUTION_MSG); 235 message->AddInt32("width", mode.width); 236 message->AddInt32("height", mode.height); 237 238 BString name; 239 name << mode.width << " x " << mode.height; 240 241 fResolutionMenu->AddItem(new BMenuItem(name.String(), message)); 242 } 243 244 rect.Set(33.0, 30.0, 171.0, 48.0); 245 fResolutionField = new BMenuField(rect, "ResolutionMenu", "Resolution:", 246 fResolutionMenu, true); 247 fResolutionField->SetDivider(55.0); 248 controlsBox->AddChild(fResolutionField); 249 250 fColorsMenu = new BPopUpMenu("colors", true, true); 251 252 for (int32 i = 0; i < kColorSpaceCount; i++) { 253 BMessage *message = new BMessage(POP_COLORS_MSG); 254 message->AddInt32("bits_per_pixel", kColorSpaces[i].bits_per_pixel); 255 message->AddInt32("space", kColorSpaces[i].space); 256 257 fColorsMenu->AddItem(new BMenuItem(kColorSpaces[i].label, message)); 258 } 259 260 rect.Set(50.0, 58.0, 171.0, 76.0); 261 fColorsField = new BMenuField(rect, "ColorsMenu", "Colors:", fColorsMenu, true); 262 fColorsField->SetDivider(38.0); 263 controlsBox->AddChild(fColorsField); 264 265 fRefreshMenu = new BPopUpMenu("refresh rate", true, true); 266 267 #ifdef USE_FIXED_REFRESH 268 for (int32 i = 0; i < kRefreshRateCount; ++i) { 269 BString name; 270 name << kRefreshRates[i] << " Hz"; 271 272 BMessage *message = new BMessage(POP_REFRESH_MSG); 273 message->AddFloat("refresh", kRefreshRates[i]); 274 275 fRefreshMenu->AddItem(new BMenuItem(name.String(), message)); 276 } 277 #endif 278 279 BMessage *message = new BMessage(POP_OTHER_REFRESH_MSG); 280 281 fOtherRefresh = new BMenuItem("Other" B_UTF8_ELLIPSIS, message); 282 fRefreshMenu->AddItem(fOtherRefresh); 283 284 rect.Set(19.0, 86.0, 171.0, 104.0); 285 fRefreshField = new BMenuField(rect, "RefreshMenu", "Refresh Rate:", fRefreshMenu, true); 286 fRefreshField->SetDivider(69.0); 287 controlsBox->AddChild(fRefreshField); 288 289 view->AddChild(controlsBox); 290 291 // enlarged area for multi-monitor settings 292 { 293 bool dummy; 294 uint32 dummy32; 295 bool multiMonSupport; 296 bool useLaptopPanelSupport; 297 bool tvStandardSupport; 298 299 multiMonSupport = TestMultiMonSupport(&screen) == B_OK; 300 useLaptopPanelSupport = GetUseLaptopPanel(&screen, &dummy) == B_OK; 301 tvStandardSupport = GetTVStandard(&screen, &dummy32) == B_OK; 302 303 // even if there is no support, we still create all controls 304 // to make sure we don't access NULL pointers later on 305 if (multiMonSupport) { 306 fApplyButton->MoveTo(275, 114); 307 controlsBox->ResizeTo(366, 148); 308 ResizeTo(556, 202); 309 } 310 311 fCombineMenu = new BPopUpMenu("CombineDisplays", true, true); 312 313 for (int32 i = 0; i < kCombineModeCount; i++) { 314 message = new BMessage(POP_COMBINE_DISPLAYS_MSG); 315 message->AddInt32("mode", kCombineModes[i].mode); 316 317 fCombineMenu->AddItem(new BMenuItem(kCombineModes[i].name, message)); 318 } 319 320 rect.Set(185, 30, 356, 48); 321 BMenuField* menuField = new BMenuField(rect, "CombineMenu", 322 "Combine Displays:", fCombineMenu, true); 323 menuField->SetDivider(90); 324 controlsBox->AddChild(menuField); 325 326 if (!multiMonSupport) 327 menuField->Hide(); 328 329 fSwapDisplaysMenu = new BPopUpMenu("SwapDisplays", true, true); 330 331 // !order is important - we rely that boolean value == idx 332 message = new BMessage(POP_SWAP_DISPLAYS_MSG); 333 message->AddBool("swap", false); 334 fSwapDisplaysMenu->AddItem(new BMenuItem("no", message)); 335 336 message = new BMessage(POP_SWAP_DISPLAYS_MSG); 337 message->AddBool("swap", true); 338 fSwapDisplaysMenu->AddItem(new BMenuItem("yes", message)); 339 340 rect.Set(199, 58, 356, 76); 341 menuField = new BMenuField(rect, "SwapMenu", "Swap Displays:", 342 fSwapDisplaysMenu, true); 343 menuField->SetDivider(76); 344 345 controlsBox->AddChild(menuField); 346 if (!multiMonSupport) 347 menuField->Hide(); 348 349 fUseLaptopPanelMenu = new BPopUpMenu("UseLaptopPanel", true, true); 350 351 // !order is important - we rely that boolean value == idx 352 message = new BMessage(POP_USE_LAPTOP_PANEL_MSG); 353 message->AddBool("use", false); 354 fUseLaptopPanelMenu->AddItem(new BMenuItem("if needed", message)); 355 356 message = new BMessage(POP_USE_LAPTOP_PANEL_MSG); 357 message->AddBool("use", true); 358 fUseLaptopPanelMenu->AddItem(new BMenuItem("always", message)); 359 360 rect.Set(184, 86, 356, 104); 361 menuField = new BMenuField(rect, "UseLaptopPanel", "Use Laptop Panel:", 362 fUseLaptopPanelMenu, true); 363 menuField->SetDivider(91); 364 365 controlsBox->AddChild(menuField); 366 if (!useLaptopPanelSupport) 367 menuField->Hide(); 368 369 fTVStandardMenu = new BPopUpMenu("TVStandard", true, true); 370 371 // arbitrary limit 372 uint32 i; 373 for (i = 0; i < 100; ++i) { 374 uint32 mode; 375 376 if (GetNthSupportedTVStandard(&screen, i, &mode) != B_OK) 377 break; 378 379 BString name = tv_standard_to_string(mode); 380 381 message = new BMessage(POP_TV_STANDARD_MSG); 382 message->AddInt32("tv_standard", mode); 383 384 fTVStandardMenu->AddItem(new BMenuItem(name.String(), message)); 385 } 386 387 rect.Set(15, 114, 171, 132); 388 menuField = new BMenuField(rect, "tv standard", "Video Format:", 389 fTVStandardMenu, true); 390 menuField->SetDivider(73); 391 392 if (!tvStandardSupport || i == 0) 393 menuField->Hide(); 394 395 controlsBox->AddChild(menuField); 396 } 397 398 rect.Set(10.0, 167, 100.0, 200.0); 399 fDefaultsButton = new BButton(rect, "DefaultsButton", "Defaults", 400 new BMessage(BUTTON_DEFAULTS_MSG)); 401 fDefaultsButton->ResizeToPreferred(); 402 view->AddChild(fDefaultsButton); 403 404 rect.Set(95.0, 167, 160.0, 200.0); 405 fRevertButton = new BButton(rect, "RevertButton", "Revert", 406 new BMessage(BUTTON_REVERT_MSG)); 407 fRevertButton->ResizeToPreferred(); 408 fRevertButton->SetEnabled(false); 409 view->AddChild(fRevertButton); 410 411 UpdateControls(); 412 } 413 414 415 ScreenWindow::~ScreenWindow() 416 { 417 delete fSettings; 418 } 419 420 421 bool 422 ScreenWindow::QuitRequested() 423 { 424 fSettings->SetWindowFrame(Frame()); 425 be_app->PostMessage(B_QUIT_REQUESTED); 426 427 return BWindow::QuitRequested(); 428 } 429 430 431 /** update resolution list according to combine mode 432 * (some resolution may not be combinable due to memory restrictions) 433 */ 434 435 void 436 ScreenWindow::CheckResolutionMenu() 437 { 438 for (int32 i = 0; i < fResolutionMenu->CountItems(); i++) 439 fResolutionMenu->ItemAt(i)->SetEnabled(false); 440 441 for (int32 i = 0; i < fScreenMode.CountModes(); i++) { 442 screen_mode mode = fScreenMode.ModeAt(i); 443 if (mode.combine != fSelected.combine) 444 continue; 445 446 BString name; 447 name << mode.width << " x " << mode.height; 448 449 BMenuItem *item = fResolutionMenu->FindItem(name.String()); 450 if (item != NULL) 451 item->SetEnabled(true); 452 } 453 } 454 455 456 /** update color and refresh options according to current mode 457 * (a color space is made active if there is any mode with 458 * given resolution and this colour space; same applies for 459 * refresh rate, though "Other…" is always possible) 460 */ 461 462 void 463 ScreenWindow::CheckColorMenu() 464 { 465 for (int32 i = 0; i < kColorSpaceCount; i++) { 466 bool supported = false; 467 468 for (int32 j = 0; j < fScreenMode.CountModes(); j++) { 469 screen_mode mode = fScreenMode.ModeAt(j); 470 471 if (fSelected.width == mode.width 472 && fSelected.height == mode.height 473 && kColorSpaces[i].space == mode.space 474 && fSelected.combine == mode.combine) { 475 supported = true; 476 break; 477 } 478 } 479 480 BMenuItem* item = fColorsMenu->ItemAt(i); 481 if (item) 482 item->SetEnabled(supported); 483 } 484 } 485 486 487 /** Enable/disable refresh options according to current mode. 488 * Only needed when USE_FIXED_REFRESH is not defined. 489 */ 490 491 void 492 ScreenWindow::CheckRefreshMenu() 493 { 494 #ifndef USE_FIXED_REFRESH 495 // ToDo: does currently not compile! 496 for (int32 i = fRefreshMenu->CountItems() - 2; i >= 0; --i) { 497 delete fRefreshMenu->RemoveItem(i); 498 } 499 500 for (int32 i = 0; i < fModeListCount; ++i) { 501 if (virtualWidth == fModeList[i].virtual_width 502 && virtualHeight == fModeList[i].virtual_height 503 && combine == get_combine_mode(&fModeList[i])) { 504 BString name; 505 BMenuItem *item; 506 507 int32 refresh10 = get_refresh10(fModeList[i]); 508 refresh10_to_string(name, refresh10); 509 510 item = fRefreshMenu->FindItem(name.String()); 511 if (item == NULL) { 512 BMessage *msg = new BMessage(POP_REFRESH_MSG); 513 msg->AddFloat("refresh", refresh); 514 515 fRefreshMenu->AddItem(new BMenuItem(name.String(), msg), 516 fRefreshMenu->CountItems() - 1); 517 } 518 } 519 } 520 #endif 521 522 // TBD: some drivers lack many refresh rates; still, they 523 // can be used by generating the mode manually 524 /* 525 for( i = 0; i < sizeof( refresh_list ) / sizeof( refresh_list[0] ); ++i ) { 526 BMenuItem *item; 527 bool supported = false; 528 529 for( j = 0; j < fModeListCount; ++j ) { 530 if( width == fModeList[j].virtual_width && 531 height == fModeList[j].virtual_height && 532 refresh_list[i].refresh * 10 == getModeRefresh10( &fModeList[j] )) 533 { 534 supported = true; 535 break; 536 } 537 } 538 539 item = fRefreshMenu->ItemAt( i ); 540 if( item ) 541 item->SetEnabled( supported ); 542 } 543 */ 544 } 545 546 547 /** activate appropriate menu item according to selected refresh rate */ 548 549 void 550 ScreenWindow::UpdateRefreshControl() 551 { 552 BString string; 553 refresh_rate_to_string(fSelected.refresh, string); 554 555 BMenuItem* item = fRefreshMenu->FindItem(string.String()); 556 if (item) { 557 if (!item->IsMarked()) 558 item->SetMarked(true); 559 // "Other…" items only contains a refresh rate when active 560 fOtherRefresh->SetLabel("Other…"); 561 return; 562 } 563 564 // this is a non-standard refresh rate 565 566 fOtherRefresh->Message()->ReplaceFloat("refresh", fSelected.refresh); 567 fOtherRefresh->SetMarked(true); 568 569 fRefreshMenu->Superitem()->SetLabel(string.String()); 570 571 string.Append("/Other" B_UTF8_ELLIPSIS); 572 fOtherRefresh->SetLabel(string.String()); 573 } 574 575 576 void 577 ScreenWindow::UpdateMonitorView() 578 { 579 BMessage updateMessage(UPDATE_DESKTOP_MSG); 580 updateMessage.AddInt32("width", fSelected.width); 581 updateMessage.AddInt32("height", fSelected.height); 582 583 PostMessage(&updateMessage, fMonitorView); 584 } 585 586 587 void 588 ScreenWindow::UpdateControls() 589 { 590 BMenuItem* item = fSwapDisplaysMenu->ItemAt((int32)fSelected.swap_displays); 591 if (item && !item->IsMarked()) 592 item->SetMarked(true); 593 594 item = fUseLaptopPanelMenu->ItemAt((int32)fSelected.use_laptop_panel); 595 if (item && !item->IsMarked()) 596 item->SetMarked(true); 597 598 for (int32 i = 0; i < fTVStandardMenu->CountItems(); i++) { 599 item = fTVStandardMenu->ItemAt(i); 600 601 uint32 tvStandard; 602 item->Message()->FindInt32("tv_standard", (int32 *)&tvStandard); 603 if (tvStandard == fSelected.tv_standard) { 604 if (!item->IsMarked()) 605 item->SetMarked(true); 606 break; 607 } 608 } 609 610 CheckResolutionMenu(); 611 CheckColorMenu(); 612 CheckRefreshMenu(); 613 614 BString string; 615 resolution_to_string(fSelected, string); 616 item = fResolutionMenu->FindItem(string.String()); 617 618 if (item != NULL) { 619 if (!item->IsMarked()) 620 item->SetMarked(true); 621 } else { 622 // this is bad luck - if mode has been set via screen references, 623 // this case cannot occur; there are three possible solutions: 624 // 1. add a new resolution to list 625 // - we had to remove it as soon as a "valid" one is selected 626 // - we don't know which frequencies/bit depths are supported 627 // - as long as we haven't the GMT formula to create 628 // parameters for any resolution given, we cannot 629 // really set current mode - it's just not in the list 630 // 2. choose nearest resolution 631 // - probably a good idea, but implies coding and testing 632 // 3. choose lowest resolution 633 // - do you really think we are so lazy? yes, we are 634 item = fResolutionMenu->ItemAt(0); 635 if (item) 636 item->SetMarked(true); 637 638 // okay - at least we set menu label to active resolution 639 fResolutionMenu->Superitem()->SetLabel(string.String()); 640 } 641 642 // mark active combine mode 643 for (int32 i = 0; i < kCombineModeCount; i++) { 644 if (kCombineModes[i].mode == fSelected.combine) { 645 item = fCombineMenu->ItemAt(i); 646 if (item && !item->IsMarked()) 647 item->SetMarked(true); 648 break; 649 } 650 } 651 652 item = fColorsMenu->ItemAt(0); 653 654 for (int32 i = kColorSpaceCount; i-- > 0;) { 655 if (kColorSpaces[i].space == fSelected.space) { 656 item = fColorsMenu->ItemAt(i); 657 break; 658 } 659 } 660 661 if (item && !item->IsMarked()) 662 item->SetMarked(true); 663 664 string.Truncate(0); 665 string << fSelected.BitsPerPixel() << " Bits/Pixel"; 666 if (string != fColorsMenu->Superitem()->Label()) 667 fColorsMenu->Superitem()->SetLabel(string.String()); 668 669 UpdateMonitorView(); 670 UpdateRefreshControl(); 671 672 CheckApplyEnabled(); 673 } 674 675 676 /** reflect active mode in chosen settings */ 677 678 void 679 ScreenWindow::UpdateActiveMode() 680 { 681 // usually, this function gets called after a mode 682 // has been set manually; still, as the graphics driver 683 // is free to fiddle with mode passed, we better ask 684 // what kind of mode we actually got 685 fScreenMode.Get(fActive); 686 fSelected = fActive; 687 688 UpdateControls(); 689 } 690 691 692 void 693 ScreenWindow::ScreenChanged(BRect frame, color_space mode) 694 { 695 // move window on screen, if necessary 696 if (frame.right <= Frame().right 697 && frame.bottom <= Frame().bottom) { 698 MoveTo((frame.Width() - Frame().Width()) / 2, 699 (frame.Height() - Frame().Height()) / 2); 700 } 701 } 702 703 704 void 705 ScreenWindow::WorkspaceActivated(int32 workspace, bool state) 706 { 707 if (fChangingAllWorkspaces) { 708 // we're currently changing all workspaces, so there is no need 709 // to update the interface 710 return; 711 } 712 713 fScreenMode.Get(fOriginal); 714 fScreenMode.UpdateOriginalMode(); 715 716 // only override current settings if they have not been changed yet 717 if (fSelected == fActive) 718 UpdateActiveMode(); 719 720 PostMessage(new BMessage(UPDATE_DESKTOP_COLOR_MSG), fMonitorView); 721 } 722 723 724 void 725 ScreenWindow::MessageReceived(BMessage* message) 726 { 727 switch (message->what) { 728 case WORKSPACE_CHECK_MSG: 729 CheckApplyEnabled(); 730 break; 731 732 case POP_WORKSPACE_CHANGED_MSG: 733 { 734 int32 index; 735 if (message->FindInt32("index", &index) == B_OK) 736 set_workspace_count(index + 1); 737 break; 738 } 739 740 case POP_RESOLUTION_MSG: 741 { 742 message->FindInt32("width", &fSelected.width); 743 message->FindInt32("height", &fSelected.height); 744 745 CheckColorMenu(); 746 CheckRefreshMenu(); 747 748 UpdateMonitorView(); 749 UpdateRefreshControl(); 750 751 CheckApplyEnabled(); 752 break; 753 } 754 755 case POP_COLORS_MSG: 756 { 757 message->FindInt32("space", (int32 *)&fSelected.space); 758 759 BString string; 760 string << fSelected.BitsPerPixel() << " Bits/Pixel"; 761 fColorsMenu->Superitem()->SetLabel(string.String()); 762 763 CheckApplyEnabled(); 764 break; 765 } 766 767 case POP_REFRESH_MSG: 768 message->FindFloat("refresh", &fSelected.refresh); 769 fOtherRefresh->SetLabel("Other" B_UTF8_ELLIPSIS); 770 // revert "Other…" label - it might have had a refresh rate prefix 771 772 CheckApplyEnabled(); 773 break; 774 775 case POP_OTHER_REFRESH_MSG: 776 { 777 // make sure menu shows something usefull 778 UpdateRefreshControl(); 779 780 BRect frame(Frame()); 781 RefreshWindow *fRefreshWindow = new RefreshWindow(BRect(frame.left + 201.0, 782 frame.top + 34.0, frame.left + 509.0, frame.top + 169.0), 783 int32(fSelected.refresh * 10)); 784 fRefreshWindow->Show(); 785 break; 786 } 787 788 case SET_CUSTOM_REFRESH_MSG: 789 { 790 // user pressed "done" in "Other…" refresh dialog; 791 // select the refresh rate chosen 792 message->FindFloat("refresh", &fSelected.refresh); 793 794 UpdateRefreshControl(); 795 CheckApplyEnabled(); 796 break; 797 } 798 799 case POP_COMBINE_DISPLAYS_MSG: 800 { 801 // new combine mode has bee chosen 802 int32 mode; 803 if (message->FindInt32("mode", &mode) == B_OK) 804 fSelected.combine = (combine_mode)mode; 805 806 CheckResolutionMenu(); 807 CheckApplyEnabled(); 808 break; 809 } 810 811 case POP_SWAP_DISPLAYS_MSG: 812 message->FindBool("swap", &fSelected.swap_displays); 813 CheckApplyEnabled(); 814 break; 815 816 case POP_USE_LAPTOP_PANEL_MSG: 817 message->FindBool("use", &fSelected.use_laptop_panel); 818 CheckApplyEnabled(); 819 break; 820 821 case POP_TV_STANDARD_MSG: 822 message->FindInt32("tv_standard", (int32 *)&fSelected.tv_standard); 823 CheckApplyEnabled(); 824 break; 825 826 case BUTTON_DEFAULTS_MSG: 827 { 828 fSelected.width = 640; 829 fSelected.height = 480; 830 fSelected.space = B_CMAP8; 831 fSelected.refresh = 60.0; 832 fSelected.combine = kCombineDisable; 833 fSelected.swap_displays = false; 834 fSelected.use_laptop_panel = false; 835 fSelected.tv_standard = 0; 836 837 UpdateControls(); 838 break; 839 } 840 841 case BUTTON_REVERT_MSG: 842 case SET_INITIAL_MODE_MSG: 843 fScreenMode.Revert(); 844 UpdateActiveMode(); 845 break; 846 847 case BUTTON_APPLY_MSG: 848 Apply(); 849 break; 850 851 case MAKE_INITIAL_MSG: { 852 // user pressed "keep" in confirmation box; 853 // select this mode in dialog and mark it as 854 // previous mode; if "all workspaces" is selected, 855 // distribute mode to all workspaces 856 857 // use the mode that has eventually been set and 858 // thus we know to be working; it can differ from 859 // the mode selected by user due to hardware limitation 860 display_mode newMode; 861 BScreen screen(this); 862 screen.GetMode(&newMode); 863 864 if (fAllWorkspacesItem->IsMarked()) { 865 int32 originatingWorkspace; 866 867 // the original panel activates each workspace in turn; 868 // this is disguisting and there is a SetMode 869 // variant that accepts a workspace id, so let's take 870 // this one 871 originatingWorkspace = current_workspace(); 872 873 // well, this "cannot be reverted" message is not 874 // entirely true - at least, you can revert it 875 // for current workspace; to not overwrite original 876 // mode during workspace switch, we use this flag 877 fChangingAllWorkspaces = true; 878 879 for (int32 i = 0; i < count_workspaces(); i++) { 880 if (i != originatingWorkspace) 881 screen.SetMode(i, &newMode, true); 882 } 883 884 fChangingAllWorkspaces = false; 885 } 886 887 UpdateActiveMode(); 888 break; 889 } 890 891 default: 892 BWindow::MessageReceived(message); 893 break; 894 } 895 } 896 897 898 bool 899 ScreenWindow::CanApply() const 900 { 901 if (fAllWorkspacesItem->IsMarked()) 902 return true; 903 904 return fSelected != fActive; 905 } 906 907 908 bool 909 ScreenWindow::CanRevert() const 910 { 911 if (fActive != fOriginal) 912 return true; 913 914 return CanApply(); 915 } 916 917 918 void 919 ScreenWindow::CheckApplyEnabled() 920 { 921 fApplyButton->SetEnabled(CanApply()); 922 fRevertButton->SetEnabled(CanRevert()); 923 } 924 925 926 void 927 ScreenWindow::Apply() 928 { 929 if (fAllWorkspacesItem->IsMarked()) { 930 BAlert *workspacesAlert = new BAlert("WorkspacesAlert", 931 "Change all workspaces? This action cannot be reverted.", "Okay", "Cancel", 932 NULL, B_WIDTH_AS_USUAL, B_WARNING_ALERT); 933 934 if (workspacesAlert->Go() == 1) 935 return; 936 } 937 938 status_t status = fScreenMode.Set(fSelected); 939 if (status == B_OK) { 940 fActive = fSelected; 941 942 // ToDo: only show alert when this is an unknown mode 943 BWindow* window = new AlertWindow(this); 944 window->Show(); 945 } else { 946 char message[256]; 947 snprintf(message, sizeof(message), 948 "The screen mode could not be set:\n\t%s\n", screen_errors(status)); 949 BAlert* alert = new BAlert("Screen Alert", message, "Okay", NULL, NULL, 950 B_WIDTH_AS_USUAL, B_WARNING_ALERT); 951 alert->Go(); 952 } 953 } 954 955