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