1 /* 2 * Copyright 2001-2006, 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 * Stephan Aßmus <superstippi@gmx.de> 12 */ 13 14 15 #include <Alert.h> 16 #include <Application.h> 17 #include <Box.h> 18 #include <Button.h> 19 #include <InterfaceDefs.h> 20 #include <MenuBar.h> 21 #include <MenuItem.h> 22 #include <MenuField.h> 23 #include <Messenger.h> 24 #include <PopUpMenu.h> 25 #include <Screen.h> 26 #include <String.h> 27 #include <Roster.h> 28 #include <Window.h> 29 30 #include <cstdio> 31 #include <cstdlib> 32 #include <cstring> 33 34 #include "AlertWindow.h" 35 #include "Constants.h" 36 #include "RefreshWindow.h" 37 #include "MonitorView.h" 38 #include "ScreenSettings.h" 39 #include "ScreenWindow.h" 40 #include "Utility.h" 41 42 /* Note, this headers defines a *private* interface to the Radeon accelerant. 43 * It's a solution that works with the current BeOS interface that Haiku 44 * adopted. 45 * However, it's not a nice and clean solution. Don't use this header in any 46 * application if you can avoid it. No other driver is using this, or should 47 * be using this. 48 * It will be replaced as soon as we introduce an updated accelerant interface 49 * which may even happen before R1 hits the streets. 50 */ 51 52 #include "multimon.h" // the usual: DANGER WILL, ROBINSON! 53 54 55 const char* kBackgroundsSignature = "application/x-vnd.haiku-backgrounds"; 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[] = { 60, 70, 72, 75, 80, 85, 95, 100 }; 72 static const int32 kRefreshRateCount = sizeof(kRefreshRates) / sizeof(kRefreshRates[0]); 73 74 // list of combine modes 75 static const struct { 76 combine_mode mode; 77 const char *name; 78 } kCombineModes[] = { 79 { kCombineDisable, "disable" }, 80 { kCombineHorizontally, "horizontally" }, 81 { kCombineVertically, "vertically" } 82 }; 83 static const int32 kCombineModeCount = sizeof(kCombineModes) / sizeof(kCombineModes[0]); 84 85 enum { 86 SHOW_COMBINE_FIELD = 0x01, 87 SHOW_SWAP_FIELD = 0x02, 88 SHOW_LAPTOP_PANEL_FIELD = 0x04, 89 SHOW_TV_STANDARD_FIELD = 0x08, 90 }; 91 92 93 static BString 94 tv_standard_to_string(uint32 mode) 95 { 96 switch (mode) { 97 case 0: return "disabled"; 98 case 1: return "NTSC"; 99 case 2: return "NTSC Japan"; 100 case 3: return "PAL BDGHI"; 101 case 4: return "PAL M"; 102 case 5: return "PAL N"; 103 case 6: return "SECAM"; 104 case 101: return "NTSC 443"; 105 case 102: return "PAL 60"; 106 case 103: return "PAL NC"; 107 default: 108 { 109 BString name; 110 name << "??? (" << mode << ")"; 111 112 return name; 113 } 114 } 115 } 116 117 118 static void 119 resolution_to_string(screen_mode& mode, BString &string) 120 { 121 string << mode.width << " x " << mode.height; 122 } 123 124 125 static void 126 refresh_rate_to_string(float refresh, BString &string, 127 bool appendUnit = true, bool alwaysWithFraction = false) 128 { 129 snprintf(string.LockBuffer(32), 32, "%.*g", refresh >= 100.0 ? 4 : 3, refresh); 130 string.UnlockBuffer(); 131 132 if (appendUnit) 133 string << " Hz"; 134 } 135 136 137 static const char* 138 screen_errors(status_t status) 139 { 140 switch (status) { 141 case B_ENTRY_NOT_FOUND: 142 return "Unknown Mode"; 143 // TODO: add more? 144 145 default: 146 return strerror(status); 147 } 148 } 149 150 151 static float 152 max_label_width(BMenuField* control, float widestLabel) 153 { 154 float labelWidth = control->StringWidth(control->Label()); 155 if (widestLabel < labelWidth) 156 return labelWidth; 157 return widestLabel; 158 } 159 160 161 static BRect 162 stack_and_align_menu_fields(const BList& menuFields) 163 { 164 float widestLabel = 0.0; 165 int32 count = menuFields.CountItems(); 166 for (int32 i = 0; i < count; i++) { 167 BMenuField* menuField = (BMenuField*)menuFields.ItemAtFast(i); 168 widestLabel = max_label_width(menuField, widestLabel); 169 } 170 171 // add some room (but only if there is text at all) 172 if (widestLabel > 0.0) 173 widestLabel += 5.0; 174 175 // make all controls the same width 176 float widestField = 0.0; 177 for (int32 i = 0; i < count; i++) { 178 BMenuField* menuField = (BMenuField*)menuFields.ItemAtFast(i); 179 menuField->SetAlignment(B_ALIGN_RIGHT); 180 menuField->SetDivider(widestLabel); 181 menuField->ResizeToPreferred(); 182 widestField = max_c(menuField->Bounds().Width(), widestField); 183 } 184 185 // layout controls under each other, resize all to size 186 // of largest of them (they could still have different 187 // heights though) 188 BMenuField* topMenuField = (BMenuField*)menuFields.FirstItem(); 189 BPoint leftTop = topMenuField->Frame().LeftTop(); 190 BRect frame = topMenuField->Frame(); 191 192 for (int32 i = 0; i < count; i++) { 193 BMenuField* menuField = (BMenuField*)menuFields.ItemAtFast(i); 194 menuField->MoveTo(leftTop); 195 float height = menuField->Bounds().Height(); 196 menuField->ResizeTo(widestField, height); 197 frame = frame | menuField->Frame(); 198 leftTop.y += height + 5.0; 199 } 200 201 return frame; 202 } 203 204 205 // #pragma mark - 206 207 208 ScreenWindow::ScreenWindow(ScreenSettings *settings) 209 : BWindow(settings->WindowFrame(), "Screen", B_TITLED_WINDOW, 210 B_NOT_RESIZABLE | B_NOT_ZOOMABLE, B_ALL_WORKSPACES), 211 fScreenMode(this), 212 fChangingAllWorkspaces(false) 213 { 214 BScreen screen(this); 215 216 fScreenMode.Get(fOriginal); 217 fActive = fSelected = fOriginal; 218 219 BView *view = new BView(Bounds(), "ScreenView", B_FOLLOW_ALL, B_WILL_DRAW); 220 view->SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR)); 221 AddChild(view); 222 223 fSettings = settings; 224 225 // we need the "Current Workspace" first to get its height 226 227 BPopUpMenu *popUpMenu = new BPopUpMenu("Current Workspace", true, true); 228 fAllWorkspacesItem = new BMenuItem("All Workspaces", new BMessage(WORKSPACE_CHECK_MSG)); 229 popUpMenu->AddItem(fAllWorkspacesItem); 230 BMenuItem *item = new BMenuItem("Current Workspace", new BMessage(WORKSPACE_CHECK_MSG)); 231 item->SetMarked(true); 232 popUpMenu->AddItem(item); 233 234 BMenuField* workspaceMenuField = new BMenuField(BRect(0, 0, 100, 15), 235 "WorkspaceMenu", NULL, popUpMenu, true); 236 workspaceMenuField->ResizeToPreferred(); 237 238 // box on the left with workspace count and monitor view 239 240 popUpMenu = new BPopUpMenu("", true, true); 241 fWorkspaceCountField = new BMenuField(BRect(0.0, 0.0, 50.0, 15.0), 242 "WorkspaceCountMenu", "Workspace count:", popUpMenu, true); 243 float labelWidth = fWorkspaceCountField->StringWidth(fWorkspaceCountField->Label()) + 5.0; 244 fWorkspaceCountField->SetDivider(labelWidth); 245 246 fScreenBox = new BBox(BRect(0.0, 0.0, 100.0, 100.0), "left box"); 247 fScreenBox->AddChild(fWorkspaceCountField); 248 249 for (int32 count = 1; count <= 32; count++) { 250 BString workspaceCount; 251 workspaceCount << count; 252 253 BMessage *message = new BMessage(POP_WORKSPACE_CHANGED_MSG); 254 message->AddInt32("workspace count", count); 255 256 popUpMenu->AddItem(new BMenuItem(workspaceCount.String(), 257 message)); 258 } 259 260 item = popUpMenu->ItemAt(count_workspaces() - 1); 261 if (item != NULL) 262 item->SetMarked(true); 263 264 fMonitorView = new MonitorView(BRect(0.0, 0.0, 80.0, 80.0), "monitor", 265 screen.Frame().Width() + 1, screen.Frame().Height() + 1); 266 fScreenBox->AddChild(fMonitorView); 267 268 view->AddChild(fScreenBox); 269 270 // box on the right with screen resolution, etc. 271 272 fControlsBox = new BBox(BRect(0.0, 0.0, 100.0, 100.0)); 273 fControlsBox->SetLabel(workspaceMenuField); 274 275 fResolutionMenu = new BPopUpMenu("resolution", true, true); 276 277 uint16 previousWidth = 0, previousHeight = 0; 278 for (int32 i = 0; i < fScreenMode.CountModes(); i++) { 279 screen_mode mode = fScreenMode.ModeAt(i); 280 281 if (mode.width == previousWidth && mode.height == previousHeight) 282 continue; 283 284 previousWidth = mode.width; 285 previousHeight = mode.height; 286 287 BMessage *message = new BMessage(POP_RESOLUTION_MSG); 288 message->AddInt32("width", mode.width); 289 message->AddInt32("height", mode.height); 290 291 BString name; 292 name << mode.width << " x " << mode.height; 293 294 fResolutionMenu->AddItem(new BMenuItem(name.String(), message)); 295 } 296 297 BRect rect(0.0, 0.0, 200.0, 15.0); 298 // fResolutionField needs to be at the correct 299 // left-top offset, because all other menu fields 300 // will be layouted relative to it 301 fResolutionField = new BMenuField(rect.OffsetToCopy(10.0, 30.0), 302 "ResolutionMenu", "Resolution:", fResolutionMenu, true); 303 fControlsBox->AddChild(fResolutionField); 304 305 fColorsMenu = new BPopUpMenu("colors", true, true); 306 307 for (int32 i = 0; i < kColorSpaceCount; i++) { 308 BMessage *message = new BMessage(POP_COLORS_MSG); 309 message->AddInt32("bits_per_pixel", kColorSpaces[i].bits_per_pixel); 310 message->AddInt32("space", kColorSpaces[i].space); 311 312 fColorsMenu->AddItem(new BMenuItem(kColorSpaces[i].label, message)); 313 } 314 315 rect.OffsetTo(B_ORIGIN); 316 317 fColorsField = new BMenuField(rect, "ColorsMenu", "Colors:", fColorsMenu, true); 318 fControlsBox->AddChild(fColorsField); 319 320 fRefreshMenu = new BPopUpMenu("refresh rate", true, true); 321 322 BMessage *message; 323 324 float min, max; 325 if (fScreenMode.GetRefreshLimits(fActive, min, max) && min == max) { 326 // This is a special case for drivers that only support a single 327 // frequency, like the VESA driver 328 BString name; 329 name << min << " Hz"; 330 331 message = new BMessage(POP_REFRESH_MSG); 332 message->AddFloat("refresh", min); 333 334 fRefreshMenu->AddItem(item = new BMenuItem(name.String(), message)); 335 item->SetEnabled(false); 336 } else { 337 for (int32 i = 0; i < kRefreshRateCount; ++i) { 338 BString name; 339 name << kRefreshRates[i] << " Hz"; 340 341 message = new BMessage(POP_REFRESH_MSG); 342 message->AddFloat("refresh", kRefreshRates[i]); 343 344 fRefreshMenu->AddItem(new BMenuItem(name.String(), message)); 345 } 346 347 message = new BMessage(POP_OTHER_REFRESH_MSG); 348 349 fOtherRefresh = new BMenuItem("Other" B_UTF8_ELLIPSIS, message); 350 fRefreshMenu->AddItem(fOtherRefresh); 351 } 352 353 fRefreshField = new BMenuField(rect, "RefreshMenu", "Refresh Rate:", fRefreshMenu, true); 354 fControlsBox->AddChild(fRefreshField); 355 356 view->AddChild(fControlsBox); 357 358 uint32 controlsFlags = 0; 359 360 // enlarged area for multi-monitor settings 361 { 362 bool dummy; 363 uint32 dummy32; 364 bool multiMonSupport; 365 bool useLaptopPanelSupport; 366 bool tvStandardSupport; 367 368 multiMonSupport = TestMultiMonSupport(&screen) == B_OK; 369 useLaptopPanelSupport = GetUseLaptopPanel(&screen, &dummy) == B_OK; 370 tvStandardSupport = GetTVStandard(&screen, &dummy32) == B_OK; 371 if (multiMonSupport) { 372 controlsFlags |= SHOW_COMBINE_FIELD; 373 controlsFlags |= SHOW_SWAP_FIELD; 374 } 375 if (useLaptopPanelSupport) 376 controlsFlags |= SHOW_LAPTOP_PANEL_FIELD; 377 378 // even if there is no support, we still create all controls 379 // to make sure we don't access NULL pointers later on 380 381 fCombineMenu = new BPopUpMenu("CombineDisplays", true, true); 382 383 for (int32 i = 0; i < kCombineModeCount; i++) { 384 message = new BMessage(POP_COMBINE_DISPLAYS_MSG); 385 message->AddInt32("mode", kCombineModes[i].mode); 386 387 fCombineMenu->AddItem(new BMenuItem(kCombineModes[i].name, message)); 388 } 389 390 fCombineField = new BMenuField(rect, "CombineMenu", 391 "Combine Displays:", fCombineMenu, true); 392 fControlsBox->AddChild(fCombineField); 393 394 if (!multiMonSupport) 395 fCombineField->Hide(); 396 397 fSwapDisplaysMenu = new BPopUpMenu("SwapDisplays", true, true); 398 399 // !order is important - we rely that boolean value == idx 400 message = new BMessage(POP_SWAP_DISPLAYS_MSG); 401 message->AddBool("swap", false); 402 fSwapDisplaysMenu->AddItem(new BMenuItem("no", message)); 403 404 message = new BMessage(POP_SWAP_DISPLAYS_MSG); 405 message->AddBool("swap", true); 406 fSwapDisplaysMenu->AddItem(new BMenuItem("yes", message)); 407 408 fSwapDisplaysField = new BMenuField(rect, "SwapMenu", "Swap Displays:", 409 fSwapDisplaysMenu, true); 410 411 fControlsBox->AddChild(fSwapDisplaysField); 412 if (!multiMonSupport) 413 fSwapDisplaysField->Hide(); 414 415 fUseLaptopPanelMenu = new BPopUpMenu("UseLaptopPanel", true, true); 416 417 // !order is important - we rely that boolean value == idx 418 message = new BMessage(POP_USE_LAPTOP_PANEL_MSG); 419 message->AddBool("use", false); 420 fUseLaptopPanelMenu->AddItem(new BMenuItem("if needed", message)); 421 422 message = new BMessage(POP_USE_LAPTOP_PANEL_MSG); 423 message->AddBool("use", true); 424 fUseLaptopPanelMenu->AddItem(new BMenuItem("always", message)); 425 426 fUseLaptopPanelField = new BMenuField(rect, "UseLaptopPanel", "Use Laptop Panel:", 427 fUseLaptopPanelMenu, true); 428 429 fControlsBox->AddChild(fUseLaptopPanelField); 430 if (!useLaptopPanelSupport) 431 fUseLaptopPanelField->Hide(); 432 433 fTVStandardMenu = new BPopUpMenu("TVStandard", true, true); 434 435 // arbitrary limit 436 uint32 i; 437 for (i = 0; i < 100; ++i) { 438 uint32 mode; 439 440 if (GetNthSupportedTVStandard(&screen, i, &mode) != B_OK) 441 break; 442 443 BString name = tv_standard_to_string(mode); 444 445 message = new BMessage(POP_TV_STANDARD_MSG); 446 message->AddInt32("tv_standard", mode); 447 448 fTVStandardMenu->AddItem(new BMenuItem(name.String(), message)); 449 } 450 451 fTVStandardField = new BMenuField(rect, "tv standard", "Video Format:", 452 fTVStandardMenu, true); 453 fTVStandardField->SetAlignment(B_ALIGN_RIGHT); 454 455 if (!tvStandardSupport || i == 0) { 456 fTVStandardField->Hide(); 457 } else { 458 controlsFlags |= SHOW_TV_STANDARD_FIELD; 459 } 460 461 fControlsBox->AddChild(fTVStandardField); 462 } 463 464 BRect buttonRect(0.0, 0.0, 30.0, 10.0); 465 fBackgroundsButton = new BButton(buttonRect, "BackgroundsButton", 466 "Set Background"B_UTF8_ELLIPSIS, new BMessage(BUTTON_LAUNCH_BACKGROUNDS_MSG)); 467 fBackgroundsButton->SetFontSize(be_plain_font->Size() * 0.9); 468 fScreenBox->AddChild(fBackgroundsButton); 469 470 fDefaultsButton = new BButton(buttonRect, "DefaultsButton", "Defaults", 471 new BMessage(BUTTON_DEFAULTS_MSG)); 472 view->AddChild(fDefaultsButton); 473 474 fRevertButton = new BButton(buttonRect, "RevertButton", "Revert", 475 new BMessage(BUTTON_REVERT_MSG)); 476 fRevertButton->SetEnabled(false); 477 view->AddChild(fRevertButton); 478 479 fApplyButton = new BButton(buttonRect, "ApplyButton", "Apply", 480 new BMessage(BUTTON_APPLY_MSG)); 481 fApplyButton->SetEnabled(false); 482 view->AddChild(fApplyButton); 483 484 UpdateControls(); 485 486 LayoutControls(controlsFlags); 487 } 488 489 490 ScreenWindow::~ScreenWindow() 491 { 492 delete fSettings; 493 } 494 495 496 bool 497 ScreenWindow::QuitRequested() 498 { 499 fSettings->SetWindowFrame(Frame()); 500 be_app->PostMessage(B_QUIT_REQUESTED); 501 502 return BWindow::QuitRequested(); 503 } 504 505 506 /** update resolution list according to combine mode 507 * (some resolution may not be combinable due to memory restrictions) 508 */ 509 510 void 511 ScreenWindow::CheckResolutionMenu() 512 { 513 for (int32 i = 0; i < fResolutionMenu->CountItems(); i++) 514 fResolutionMenu->ItemAt(i)->SetEnabled(false); 515 516 for (int32 i = 0; i < fScreenMode.CountModes(); i++) { 517 screen_mode mode = fScreenMode.ModeAt(i); 518 if (mode.combine != fSelected.combine) 519 continue; 520 521 BString name; 522 name << mode.width << " x " << mode.height; 523 524 BMenuItem *item = fResolutionMenu->FindItem(name.String()); 525 if (item != NULL) 526 item->SetEnabled(true); 527 } 528 } 529 530 531 /** update color and refresh options according to current mode 532 * (a color space is made active if there is any mode with 533 * given resolution and this colour space; same applies for 534 * refresh rate, though "Other…" is always possible) 535 */ 536 537 void 538 ScreenWindow::CheckColorMenu() 539 { 540 for (int32 i = 0; i < kColorSpaceCount; i++) { 541 bool supported = false; 542 543 for (int32 j = 0; j < fScreenMode.CountModes(); j++) { 544 screen_mode mode = fScreenMode.ModeAt(j); 545 546 if (fSelected.width == mode.width 547 && fSelected.height == mode.height 548 && kColorSpaces[i].space == mode.space 549 && fSelected.combine == mode.combine) { 550 supported = true; 551 break; 552 } 553 } 554 555 BMenuItem* item = fColorsMenu->ItemAt(i); 556 if (item) 557 item->SetEnabled(supported); 558 } 559 } 560 561 562 /** Enable/disable refresh options according to current mode. */ 563 564 void 565 ScreenWindow::CheckRefreshMenu() 566 { 567 float min, max; 568 if (fScreenMode.GetRefreshLimits(fSelected, min, max) != B_OK || min == max) 569 return; 570 571 for (int32 i = fRefreshMenu->CountItems(); i-- > 0;) { 572 BMenuItem* item = fRefreshMenu->ItemAt(i); 573 BMessage* message = item->Message(); 574 float refresh; 575 if (message != NULL && message->FindFloat("refresh", &refresh) == B_OK) 576 item->SetEnabled(refresh >= min && refresh <= max); 577 } 578 } 579 580 581 /** Activate appropriate menu item according to selected refresh rate */ 582 583 void 584 ScreenWindow::UpdateRefreshControl() 585 { 586 BString string; 587 refresh_rate_to_string(fSelected.refresh, string); 588 589 BMenuItem* item = fRefreshMenu->FindItem(string.String()); 590 if (item) { 591 if (!item->IsMarked()) 592 item->SetMarked(true); 593 594 // "Other…" items only contains a refresh rate when active 595 fOtherRefresh->SetLabel("Other…"); 596 return; 597 } 598 599 // this is a non-standard refresh rate 600 601 fOtherRefresh->Message()->ReplaceFloat("refresh", fSelected.refresh); 602 fOtherRefresh->SetMarked(true); 603 604 fRefreshMenu->Superitem()->SetLabel(string.String()); 605 606 string.Append("/Other" B_UTF8_ELLIPSIS); 607 fOtherRefresh->SetLabel(string.String()); 608 } 609 610 611 void 612 ScreenWindow::UpdateMonitorView() 613 { 614 BMessage updateMessage(UPDATE_DESKTOP_MSG); 615 updateMessage.AddInt32("width", fSelected.width); 616 updateMessage.AddInt32("height", fSelected.height); 617 618 PostMessage(&updateMessage, fMonitorView); 619 } 620 621 622 void 623 ScreenWindow::UpdateControls() 624 { 625 BMenuItem* item = fSwapDisplaysMenu->ItemAt((int32)fSelected.swap_displays); 626 if (item && !item->IsMarked()) 627 item->SetMarked(true); 628 629 item = fUseLaptopPanelMenu->ItemAt((int32)fSelected.use_laptop_panel); 630 if (item && !item->IsMarked()) 631 item->SetMarked(true); 632 633 for (int32 i = 0; i < fTVStandardMenu->CountItems(); i++) { 634 item = fTVStandardMenu->ItemAt(i); 635 636 uint32 tvStandard; 637 item->Message()->FindInt32("tv_standard", (int32 *)&tvStandard); 638 if (tvStandard == fSelected.tv_standard) { 639 if (!item->IsMarked()) 640 item->SetMarked(true); 641 break; 642 } 643 } 644 645 CheckResolutionMenu(); 646 CheckColorMenu(); 647 CheckRefreshMenu(); 648 649 BString string; 650 resolution_to_string(fSelected, string); 651 item = fResolutionMenu->FindItem(string.String()); 652 653 if (item != NULL) { 654 if (!item->IsMarked()) 655 item->SetMarked(true); 656 } else { 657 // this is bad luck - if mode has been set via screen references, 658 // this case cannot occur; there are three possible solutions: 659 // 1. add a new resolution to list 660 // - we had to remove it as soon as a "valid" one is selected 661 // - we don't know which frequencies/bit depths are supported 662 // - as long as we haven't the GMT formula to create 663 // parameters for any resolution given, we cannot 664 // really set current mode - it's just not in the list 665 // 2. choose nearest resolution 666 // - probably a good idea, but implies coding and testing 667 // 3. choose lowest resolution 668 // - do you really think we are so lazy? yes, we are 669 item = fResolutionMenu->ItemAt(0); 670 if (item) 671 item->SetMarked(true); 672 673 // okay - at least we set menu label to active resolution 674 fResolutionMenu->Superitem()->SetLabel(string.String()); 675 } 676 677 // mark active combine mode 678 for (int32 i = 0; i < kCombineModeCount; i++) { 679 if (kCombineModes[i].mode == fSelected.combine) { 680 item = fCombineMenu->ItemAt(i); 681 if (item && !item->IsMarked()) 682 item->SetMarked(true); 683 break; 684 } 685 } 686 687 item = fColorsMenu->ItemAt(0); 688 689 for (int32 i = kColorSpaceCount; i-- > 0;) { 690 if (kColorSpaces[i].space == fSelected.space) { 691 item = fColorsMenu->ItemAt(i); 692 break; 693 } 694 } 695 696 if (item && !item->IsMarked()) 697 item->SetMarked(true); 698 699 string.Truncate(0); 700 string << fSelected.BitsPerPixel() << " Bits/Pixel"; 701 if (string != fColorsMenu->Superitem()->Label()) 702 fColorsMenu->Superitem()->SetLabel(string.String()); 703 704 UpdateMonitorView(); 705 UpdateRefreshControl(); 706 707 CheckApplyEnabled(); 708 } 709 710 711 /** reflect active mode in chosen settings */ 712 713 void 714 ScreenWindow::UpdateActiveMode() 715 { 716 // usually, this function gets called after a mode 717 // has been set manually; still, as the graphics driver 718 // is free to fiddle with mode passed, we better ask 719 // what kind of mode we actually got 720 fScreenMode.Get(fActive); 721 fSelected = fActive; 722 723 UpdateControls(); 724 } 725 726 727 void 728 ScreenWindow::ScreenChanged(BRect frame, color_space mode) 729 { 730 // move window on screen, if necessary 731 if (frame.right <= Frame().right 732 && frame.bottom <= Frame().bottom) { 733 MoveTo((frame.Width() - Frame().Width()) / 2, 734 (frame.Height() - Frame().Height()) / 2); 735 } 736 } 737 738 739 void 740 ScreenWindow::WorkspaceActivated(int32 workspace, bool state) 741 { 742 if (fChangingAllWorkspaces) { 743 // we're currently changing all workspaces, so there is no need 744 // to update the interface 745 return; 746 } 747 748 fScreenMode.Get(fOriginal); 749 fScreenMode.UpdateOriginalMode(); 750 751 // only override current settings if they have not been changed yet 752 if (fSelected == fActive) 753 UpdateActiveMode(); 754 755 BMessage message(UPDATE_DESKTOP_COLOR_MSG); 756 PostMessage(&message, fMonitorView); 757 } 758 759 760 void 761 ScreenWindow::MessageReceived(BMessage* message) 762 { 763 switch (message->what) { 764 case WORKSPACE_CHECK_MSG: 765 CheckApplyEnabled(); 766 break; 767 768 case POP_WORKSPACE_CHANGED_MSG: 769 { 770 int32 index; 771 if (message->FindInt32("index", &index) == B_OK) 772 set_workspace_count(index + 1); 773 break; 774 } 775 776 case POP_RESOLUTION_MSG: 777 { 778 message->FindInt32("width", &fSelected.width); 779 message->FindInt32("height", &fSelected.height); 780 781 CheckColorMenu(); 782 CheckRefreshMenu(); 783 784 UpdateMonitorView(); 785 UpdateRefreshControl(); 786 787 CheckApplyEnabled(); 788 break; 789 } 790 791 case POP_COLORS_MSG: 792 { 793 message->FindInt32("space", (int32 *)&fSelected.space); 794 795 BString string; 796 string << fSelected.BitsPerPixel() << " Bits/Pixel"; 797 fColorsMenu->Superitem()->SetLabel(string.String()); 798 799 CheckApplyEnabled(); 800 break; 801 } 802 803 case POP_REFRESH_MSG: 804 message->FindFloat("refresh", &fSelected.refresh); 805 fOtherRefresh->SetLabel("Other" B_UTF8_ELLIPSIS); 806 // revert "Other…" label - it might have had a refresh rate prefix 807 808 CheckApplyEnabled(); 809 break; 810 811 case POP_OTHER_REFRESH_MSG: 812 { 813 // make sure menu shows something useful 814 UpdateRefreshControl(); 815 816 float min = 0, max = 999; 817 fScreenMode.GetRefreshLimits(fSelected, min, max); 818 if (min < gMinRefresh) 819 min = gMinRefresh; 820 if (max > gMaxRefresh) 821 max = gMaxRefresh; 822 823 RefreshWindow *fRefreshWindow = new RefreshWindow( 824 fRefreshField->ConvertToScreen(B_ORIGIN), fSelected.refresh, min, max); 825 fRefreshWindow->Show(); 826 break; 827 } 828 829 case SET_CUSTOM_REFRESH_MSG: 830 { 831 // user pressed "done" in "Other…" refresh dialog; 832 // select the refresh rate chosen 833 message->FindFloat("refresh", &fSelected.refresh); 834 835 UpdateRefreshControl(); 836 CheckApplyEnabled(); 837 break; 838 } 839 840 case POP_COMBINE_DISPLAYS_MSG: 841 { 842 // new combine mode has bee chosen 843 int32 mode; 844 if (message->FindInt32("mode", &mode) == B_OK) 845 fSelected.combine = (combine_mode)mode; 846 847 CheckResolutionMenu(); 848 CheckApplyEnabled(); 849 break; 850 } 851 852 case POP_SWAP_DISPLAYS_MSG: 853 message->FindBool("swap", &fSelected.swap_displays); 854 CheckApplyEnabled(); 855 break; 856 857 case POP_USE_LAPTOP_PANEL_MSG: 858 message->FindBool("use", &fSelected.use_laptop_panel); 859 CheckApplyEnabled(); 860 break; 861 862 case POP_TV_STANDARD_MSG: 863 message->FindInt32("tv_standard", (int32 *)&fSelected.tv_standard); 864 CheckApplyEnabled(); 865 break; 866 867 case BUTTON_LAUNCH_BACKGROUNDS_MSG: 868 if (be_roster->Launch(kBackgroundsSignature) == B_ALREADY_RUNNING) { 869 app_info info; 870 be_roster->GetAppInfo(kBackgroundsSignature, &info); 871 be_roster->ActivateApp(info.team); 872 } 873 break; 874 875 case BUTTON_DEFAULTS_MSG: 876 { 877 fSelected.width = 640; 878 fSelected.height = 480; 879 fSelected.space = B_CMAP8; 880 fSelected.refresh = 60.0; 881 fSelected.combine = kCombineDisable; 882 fSelected.swap_displays = false; 883 fSelected.use_laptop_panel = false; 884 fSelected.tv_standard = 0; 885 886 UpdateControls(); 887 break; 888 } 889 890 case BUTTON_REVERT_MSG: 891 case SET_INITIAL_MODE_MSG: 892 fScreenMode.Revert(); 893 UpdateActiveMode(); 894 break; 895 896 case BUTTON_APPLY_MSG: 897 Apply(); 898 break; 899 900 case MAKE_INITIAL_MSG: { 901 // user pressed "keep" in confirmation box; 902 // select this mode in dialog and mark it as 903 // previous mode; if "all workspaces" is selected, 904 // distribute mode to all workspaces 905 906 // use the mode that has eventually been set and 907 // thus we know to be working; it can differ from 908 // the mode selected by user due to hardware limitation 909 display_mode newMode; 910 BScreen screen(this); 911 screen.GetMode(&newMode); 912 913 if (fAllWorkspacesItem->IsMarked()) { 914 int32 originatingWorkspace; 915 916 // the original panel activates each workspace in turn; 917 // this is disguisting and there is a SetMode 918 // variant that accepts a workspace id, so let's take 919 // this one 920 originatingWorkspace = current_workspace(); 921 922 // well, this "cannot be reverted" message is not 923 // entirely true - at least, you can revert it 924 // for current workspace; to not overwrite original 925 // mode during workspace switch, we use this flag 926 fChangingAllWorkspaces = true; 927 928 for (int32 i = 0; i < count_workspaces(); i++) { 929 if (i != originatingWorkspace) 930 screen.SetMode(i, &newMode, true); 931 } 932 933 fChangingAllWorkspaces = false; 934 } 935 936 fScreenMode.UpdateOriginalMode(); 937 UpdateActiveMode(); 938 break; 939 } 940 941 default: 942 BWindow::MessageReceived(message); 943 break; 944 } 945 } 946 947 948 bool 949 ScreenWindow::CanApply() const 950 { 951 if (fAllWorkspacesItem->IsMarked()) 952 return true; 953 954 return fSelected != fActive; 955 } 956 957 958 bool 959 ScreenWindow::CanRevert() const 960 { 961 if (fActive != fOriginal) 962 return true; 963 964 return CanApply(); 965 } 966 967 968 void 969 ScreenWindow::CheckApplyEnabled() 970 { 971 fApplyButton->SetEnabled(CanApply()); 972 fRevertButton->SetEnabled(CanRevert()); 973 } 974 975 976 void 977 ScreenWindow::Apply() 978 { 979 if (fAllWorkspacesItem->IsMarked()) { 980 BAlert *workspacesAlert = new BAlert("WorkspacesAlert", 981 "Change all workspaces? This action cannot be reverted.", "Okay", "Cancel", 982 NULL, B_WIDTH_AS_USUAL, B_WARNING_ALERT); 983 984 if (workspacesAlert->Go() == 1) 985 return; 986 } 987 988 status_t status = fScreenMode.Set(fSelected); 989 if (status == B_OK) { 990 fActive = fSelected; 991 992 // ToDo: only show alert when this is an unknown mode 993 BWindow* window = new AlertWindow(this); 994 window->Show(); 995 } else { 996 char message[256]; 997 snprintf(message, sizeof(message), 998 "The screen mode could not be set:\n\t%s\n", screen_errors(status)); 999 BAlert* alert = new BAlert("Screen Alert", message, "Okay", NULL, NULL, 1000 B_WIDTH_AS_USUAL, B_WARNING_ALERT); 1001 alert->Go(); 1002 } 1003 } 1004 1005 1006 void 1007 ScreenWindow::LayoutControls(uint32 flags) 1008 { 1009 // layout the screen box and its controls 1010 fWorkspaceCountField->ResizeToPreferred(); 1011 1012 float monitorViewHeight = fMonitorView->Bounds().Height(); 1013 float workspaceFieldHeight = fWorkspaceCountField->Bounds().Height(); 1014 float backgroundsButtonHeight = fBackgroundsButton->Bounds().Height(); 1015 1016 float screenBoxWidth = fWorkspaceCountField->Bounds().Width() + 20.0; 1017 float screenBoxHeight = monitorViewHeight + 5.0 1018 + workspaceFieldHeight + 5.0 1019 + backgroundsButtonHeight 1020 + 20.0; 1021 1022 #ifdef __HAIKU__ 1023 fScreenBox->MoveTo(10.0, 10.0 + fControlsBox->TopBorderOffset()); 1024 #else 1025 fScreenBox->MoveTo(10.0, 10.0 + 3); 1026 #endif 1027 fScreenBox->ResizeTo(screenBoxWidth, screenBoxHeight); 1028 1029 float leftOffset = 10.0; 1030 float topOffset = 10.0; 1031 fMonitorView->MoveTo(leftOffset, topOffset); 1032 fMonitorView->ResizeTo(screenBoxWidth - 20.0, monitorViewHeight); 1033 fMonitorView->SetResizingMode(B_FOLLOW_ALL); 1034 topOffset += monitorViewHeight + 5.0; 1035 1036 fWorkspaceCountField->MoveTo(leftOffset, topOffset); 1037 fWorkspaceCountField->SetResizingMode(B_FOLLOW_LEFT_RIGHT | B_FOLLOW_BOTTOM); 1038 topOffset += workspaceFieldHeight + 5.0; 1039 1040 fBackgroundsButton->MoveTo(leftOffset, topOffset); 1041 fBackgroundsButton->ResizeTo(screenBoxWidth - 20.0, backgroundsButtonHeight); 1042 fBackgroundsButton->SetResizingMode(B_FOLLOW_LEFT_RIGHT | B_FOLLOW_BOTTOM); 1043 1044 fControlsBox->MoveTo(fScreenBox->Frame().right + 10.0, 10.0); 1045 1046 // layout the right side 1047 BRect controlsRect = LayoutMenuFields(flags); 1048 controlsRect.InsetBy(-10.0, -10.0); 1049 // adjust size of controls box and move aligned buttons along 1050 float xDiff = controlsRect.right - fControlsBox->Bounds().right; 1051 float yDiff = controlsRect.bottom - fControlsBox->Bounds().bottom; 1052 if (yDiff < 0.0) { 1053 // don't shrink vertically 1054 yDiff = 0.0; 1055 } 1056 1057 fControlsBox->ResizeBy(xDiff, yDiff); 1058 1059 // align bottom of boxen 1060 float boxBottomDiff = fControlsBox->Frame().bottom - fScreenBox->Frame().bottom; 1061 if (boxBottomDiff > 0) 1062 fScreenBox->ResizeBy(0.0, boxBottomDiff); 1063 else 1064 fControlsBox->ResizeBy(0.0, -boxBottomDiff); 1065 1066 BRect boxFrame = fScreenBox->Frame() | fControlsBox->Frame(); 1067 1068 // layout rest of buttons 1069 fDefaultsButton->ResizeToPreferred(); 1070 fDefaultsButton->MoveTo(boxFrame.left, boxFrame.bottom + 8); 1071 1072 fRevertButton->ResizeToPreferred(); 1073 fRevertButton->MoveTo(fDefaultsButton->Frame().right + 10, 1074 fDefaultsButton->Frame().top); 1075 1076 fApplyButton->ResizeToPreferred(); 1077 fApplyButton->MoveTo(boxFrame.right - fApplyButton->Bounds().Width(), 1078 fDefaultsButton->Frame().top); 1079 1080 ResizeTo(boxFrame.right + 10, fDefaultsButton->Frame().bottom + 10); 1081 } 1082 1083 1084 BRect 1085 ScreenWindow::LayoutMenuFields(uint32 flags, bool sideBySide) 1086 { 1087 BList menuFields; 1088 menuFields.AddItem((void*)fResolutionField); 1089 menuFields.AddItem((void*)fColorsField); 1090 menuFields.AddItem((void*)fRefreshField); 1091 1092 BRect frame; 1093 1094 if (sideBySide) 1095 frame = stack_and_align_menu_fields(menuFields); 1096 1097 if (sideBySide) 1098 menuFields.MakeEmpty(); 1099 1100 if (flags & SHOW_COMBINE_FIELD) 1101 menuFields.AddItem((void*)fCombineField); 1102 if (flags & SHOW_SWAP_FIELD) 1103 menuFields.AddItem((void*)fSwapDisplaysField); 1104 if (flags & SHOW_LAPTOP_PANEL_FIELD) 1105 menuFields.AddItem((void*)fUseLaptopPanelField); 1106 if (flags & SHOW_TV_STANDARD_FIELD) 1107 menuFields.AddItem((void*)fTVStandardField); 1108 1109 if (sideBySide) { 1110 if (menuFields.CountItems() > 0) { 1111 ((BMenuField*)menuFields.FirstItem())->MoveTo(frame.right + 8.0, frame.top); 1112 frame = frame | stack_and_align_menu_fields(menuFields); 1113 } 1114 } else { 1115 frame = stack_and_align_menu_fields(menuFields); 1116 } 1117 1118 return frame; 1119 } 1120 1121