1 /* 2 * Copyright 2001-2015 Haiku, Inc. All rights reserved. 3 * Distributed under the terms of the MIT License. 4 * 5 * Authors: 6 * Stephan Aßmus, superstippi@gmx.de 7 * Andrew Bachmann 8 * Stefano Ceccherini, burton666@libero.it 9 * Alexandre Deckner, alex@zappotek.com 10 * Axel Dörfler, axeld@pinc-software.de 11 * Rene Gollent, rene@gollent.com 12 * Thomas Kurschel 13 * Rafael Romo 14 * John Scipione, jscipione@gmail.com 15 */ 16 17 18 #include "ScreenWindow.h" 19 20 #include <stdio.h> 21 #include <stdlib.h> 22 #include <strings.h> 23 24 #include <Alert.h> 25 #include <Application.h> 26 #include <Box.h> 27 #include <Button.h> 28 #include <Catalog.h> 29 #include <ControlLook.h> 30 #include <Directory.h> 31 #include <File.h> 32 #include <FindDirectory.h> 33 #include <InterfaceDefs.h> 34 #include <LayoutBuilder.h> 35 #include <MenuBar.h> 36 #include <MenuField.h> 37 #include <MenuItem.h> 38 #include <Messenger.h> 39 #include <Path.h> 40 #include <PopUpMenu.h> 41 #include <Roster.h> 42 #include <Screen.h> 43 #include <SpaceLayoutItem.h> 44 #include <Spinner.h> 45 #include <String.h> 46 #include <StringView.h> 47 #include <Window.h> 48 49 #include <InterfacePrivate.h> 50 51 #include "AlertWindow.h" 52 #include "Constants.h" 53 #include "RefreshWindow.h" 54 #include "MonitorView.h" 55 #include "ScreenSettings.h" 56 #include "Utility.h" 57 58 /* Note, this headers defines a *private* interface to the Radeon accelerant. 59 * It's a solution that works with the current BeOS interface that Haiku 60 * adopted. 61 * However, it's not a nice and clean solution. Don't use this header in any 62 * application if you can avoid it. No other driver is using this, or should 63 * be using this. 64 * It will be replaced as soon as we introduce an updated accelerant interface 65 * which may even happen before R1 hits the streets. 66 */ 67 #include "multimon.h" // the usual: DANGER WILL, ROBINSON! 68 69 70 #undef B_TRANSLATION_CONTEXT 71 #define B_TRANSLATION_CONTEXT "Screen" 72 73 74 const char* kBackgroundsSignature = "application/x-vnd.Haiku-Backgrounds"; 75 76 // list of officially supported colour spaces 77 static const struct { 78 color_space space; 79 int32 bits_per_pixel; 80 const char* label; 81 } kColorSpaces[] = { 82 { B_CMAP8, 8, B_TRANSLATE("8 bits/pixel, 256 colors") }, 83 { B_RGB15, 15, B_TRANSLATE("15 bits/pixel, 32768 colors") }, 84 { B_RGB16, 16, B_TRANSLATE("16 bits/pixel, 65536 colors") }, 85 { B_RGB24, 24, B_TRANSLATE("24 bits/pixel, 16 Million colors") }, 86 { B_RGB32, 32, B_TRANSLATE("32 bits/pixel, 16 Million colors") } 87 }; 88 static const int32 kColorSpaceCount = B_COUNT_OF(kColorSpaces); 89 90 // list of standard refresh rates 91 static const int32 kRefreshRates[] = { 60, 70, 72, 75, 80, 85, 95, 100 }; 92 static const int32 kRefreshRateCount = B_COUNT_OF(kRefreshRates); 93 94 // list of combine modes 95 static const struct { 96 combine_mode mode; 97 const char *name; 98 } kCombineModes[] = { 99 { kCombineDisable, B_TRANSLATE("disable") }, 100 { kCombineHorizontally, B_TRANSLATE("horizontally") }, 101 { kCombineVertically, B_TRANSLATE("vertically") } 102 }; 103 static const int32 kCombineModeCount = B_COUNT_OF(kCombineModes); 104 105 106 static BString 107 tv_standard_to_string(uint32 mode) 108 { 109 switch (mode) { 110 case 0: return "disabled"; 111 case 1: return "NTSC"; 112 case 2: return "NTSC Japan"; 113 case 3: return "PAL BDGHI"; 114 case 4: return "PAL M"; 115 case 5: return "PAL N"; 116 case 6: return "SECAM"; 117 case 101: return "NTSC 443"; 118 case 102: return "PAL 60"; 119 case 103: return "PAL NC"; 120 default: 121 { 122 BString name; 123 name << "??? (" << mode << ")"; 124 125 return name; 126 } 127 } 128 } 129 130 131 static void 132 resolution_to_string(screen_mode& mode, BString &string) 133 { 134 string.SetToFormat(B_TRANSLATE_COMMENT("%" B_PRId32" × %" B_PRId32, 135 "The '×' is the Unicode multiplication sign U+00D7"), 136 mode.width, mode.height); 137 } 138 139 140 static void 141 refresh_rate_to_string(float refresh, BString &string, 142 bool appendUnit = true, bool alwaysWithFraction = false) 143 { 144 snprintf(string.LockBuffer(32), 32, "%.*g", refresh >= 100.0 ? 4 : 3, 145 refresh); 146 string.UnlockBuffer(); 147 148 if (appendUnit) 149 string << " " << B_TRANSLATE("Hz"); 150 } 151 152 153 static const char* 154 screen_errors(status_t status) 155 { 156 switch (status) { 157 case B_ENTRY_NOT_FOUND: 158 return B_TRANSLATE("Unknown mode"); 159 // TODO: add more? 160 161 default: 162 return strerror(status); 163 } 164 } 165 166 167 // #pragma mark - ScreenWindow 168 169 170 ScreenWindow::ScreenWindow(ScreenSettings* settings) 171 : 172 BWindow(settings->WindowFrame(), B_TRANSLATE_SYSTEM_NAME("Screen"), 173 B_TITLED_WINDOW, B_NOT_RESIZABLE | B_NOT_ZOOMABLE 174 | B_AUTO_UPDATE_SIZE_LIMITS, B_ALL_WORKSPACES), 175 fIsVesa(false), 176 fBootWorkspaceApplied(false), 177 fUserSelectedColorSpace(NULL), 178 fOtherRefresh(NULL), 179 fScreenMode(this), 180 fUndoScreenMode(this), 181 fModified(false) 182 { 183 BScreen screen(this); 184 185 accelerant_device_info info; 186 if (screen.GetDeviceInfo(&info) == B_OK 187 && !strcasecmp(info.chipset, "VESA")) 188 fIsVesa = true; 189 190 _UpdateOriginal(); 191 _BuildSupportedColorSpaces(); 192 fActive = fSelected = fOriginal; 193 194 fSettings = settings; 195 196 // we need the "Current Workspace" first to get its height 197 198 BPopUpMenu* popUpMenu = new BPopUpMenu(B_TRANSLATE("Current workspace"), 199 true, true); 200 fAllWorkspacesItem = new BMenuItem(B_TRANSLATE("All workspaces"), 201 new BMessage(WORKSPACE_CHECK_MSG)); 202 popUpMenu->AddItem(fAllWorkspacesItem); 203 BMenuItem *item = new BMenuItem(B_TRANSLATE("Current workspace"), 204 new BMessage(WORKSPACE_CHECK_MSG)); 205 206 popUpMenu->AddItem(item); 207 fAllWorkspacesItem->SetMarked(true); 208 209 BMenuField* workspaceMenuField = new BMenuField("WorkspaceMenu", NULL, 210 popUpMenu); 211 workspaceMenuField->ResizeToPreferred(); 212 213 // box on the left with workspace count and monitor view 214 215 fScreenBox = new BBox("screen box"); 216 BGroupView* groupView = new BGroupView(B_VERTICAL, B_USE_SMALL_SPACING); 217 fScreenBox->AddChild(groupView); 218 fScreenBox->SetLabel("placeholder"); 219 // Needed for layouting, will be replaced with screen name/size 220 groupView->GroupLayout()->SetInsets(B_USE_DEFAULT_SPACING, 221 B_USE_DEFAULT_SPACING, B_USE_DEFAULT_SPACING, B_USE_DEFAULT_SPACING); 222 223 fDeviceInfo = new BStringView("device info", ""); 224 fDeviceInfo->SetAlignment(B_ALIGN_CENTER); 225 groupView->AddChild(fDeviceInfo); 226 227 float scaling = std::max(1.0f, be_plain_font->Size() / 12.0f); 228 fMonitorView = new MonitorView(BRect(0.0, 0.0, 80.0 * scaling, 229 80.0 * scaling), "monitor", screen.Frame().IntegerWidth() + 1, 230 screen.Frame().IntegerHeight() + 1); 231 fMonitorView->SetToolTip(B_TRANSLATE("Set background" B_UTF8_ELLIPSIS)); 232 groupView->AddChild(fMonitorView); 233 234 // brightness slider 235 fBrightnessSlider = new BSlider("brightness", B_TRANSLATE("Brightness:"), 236 NULL, 0, 255, B_HORIZONTAL); 237 groupView->AddChild(fBrightnessSlider); 238 239 if (screen.GetBrightness(&fOriginalBrightness) == B_OK) { 240 fBrightnessSlider->SetModificationMessage( 241 new BMessage(SLIDER_BRIGHTNESS_MSG)); 242 fBrightnessSlider->SetValue(fOriginalBrightness * 255); 243 } else { 244 // The driver does not support changing the brightness, 245 // so hide the slider 246 fBrightnessSlider->Hide(); 247 fOriginalBrightness = -1; 248 } 249 250 // box on the left below the screen box with workspaces 251 252 BBox* workspacesBox = new BBox("workspaces box"); 253 workspacesBox->SetLabel(B_TRANSLATE("Workspaces")); 254 255 BGroupLayout* workspacesLayout = new BGroupLayout(B_VERTICAL); 256 workspacesLayout->SetInsets(B_USE_DEFAULT_SPACING, 257 be_control_look->DefaultItemSpacing() * 2, B_USE_DEFAULT_SPACING, 258 B_USE_DEFAULT_SPACING); 259 workspacesBox->SetLayout(workspacesLayout); 260 261 fColumnsControl = new BSpinner("columns", B_TRANSLATE("Columns:"), 262 new BMessage(kMsgWorkspaceColumnsChanged)); 263 fColumnsControl->SetAlignment(B_ALIGN_RIGHT); 264 fColumnsControl->SetRange(1, 32); 265 266 fRowsControl = new BSpinner("rows", B_TRANSLATE("Rows:"), 267 new BMessage(kMsgWorkspaceRowsChanged)); 268 fRowsControl->SetAlignment(B_ALIGN_RIGHT); 269 fRowsControl->SetRange(1, 32); 270 271 uint32 columns; 272 uint32 rows; 273 BPrivate::get_workspaces_layout(&columns, &rows); 274 fColumnsControl->SetValue(columns); 275 fRowsControl->SetValue(rows); 276 277 workspacesBox->AddChild(BLayoutBuilder::Group<>() 278 .AddGroup(B_VERTICAL, B_USE_SMALL_SPACING) 279 .AddGroup(B_HORIZONTAL, 0) 280 .AddGlue() 281 .AddGrid(B_USE_DEFAULT_SPACING, B_USE_SMALL_SPACING) 282 // columns 283 .Add(fColumnsControl->CreateLabelLayoutItem(), 0, 0) 284 .Add(fColumnsControl->CreateTextViewLayoutItem(), 1, 0) 285 // rows 286 .Add(fRowsControl->CreateLabelLayoutItem(), 0, 1) 287 .Add(fRowsControl->CreateTextViewLayoutItem(), 1, 1) 288 .End() 289 .AddGlue() 290 .End() 291 .End() 292 .View()); 293 294 // put workspaces slider in a vertical group with a half space above so 295 // if hidden you won't see the extra space. 296 BView* workspacesView = BLayoutBuilder::Group<>(B_VERTICAL, 0) 297 .AddStrut(B_USE_HALF_ITEM_SPACING) 298 .Add(workspacesBox) 299 .View(); 300 301 // box on the right with screen resolution, etc. 302 303 BBox* controlsBox = new BBox("controls box"); 304 controlsBox->SetLabel(workspaceMenuField); 305 BGroupView* outerControlsView = new BGroupView(B_VERTICAL); 306 outerControlsView->GroupLayout()->SetInsets(B_USE_DEFAULT_SPACING, 307 B_USE_DEFAULT_SPACING, B_USE_DEFAULT_SPACING, B_USE_DEFAULT_SPACING); 308 controlsBox->AddChild(outerControlsView); 309 310 menu_layout layout = B_ITEMS_IN_COLUMN; 311 312 // There are modes in the list with the same resolution but different bpp or refresh rates. 313 // We don't want to take these into account when computing the menu layout, so we need to 314 // count how many entries we will really have in the menu. 315 int fullModeCount = fScreenMode.CountModes(); 316 int modeCount = 0; 317 int index = 0; 318 uint16 maxWidth = 0; 319 uint16 maxHeight = 0; 320 uint16 previousWidth = 0; 321 uint16 previousHeight = 0; 322 for (int32 i = 0; i < fullModeCount; i++) { 323 screen_mode mode = fScreenMode.ModeAt(i); 324 325 if (mode.width == previousWidth && mode.height == previousHeight) 326 continue; 327 modeCount++; 328 previousWidth = mode.width; 329 previousHeight = mode.height; 330 if (maxWidth < mode.width) 331 maxWidth = mode.width; 332 if (maxHeight < mode.height) 333 maxHeight = mode.height; 334 } 335 336 if (modeCount > 16) 337 layout = B_ITEMS_IN_MATRIX; 338 339 fResolutionMenu = new BPopUpMenu("resolution", true, true, layout); 340 341 // Compute the size we should allocate to each item in the menu 342 BRect itemRect; 343 if (layout == B_ITEMS_IN_MATRIX) { 344 BFont menuFont; 345 font_height fontHeight; 346 347 fResolutionMenu->GetFont(&menuFont); 348 menuFont.GetHeight(&fontHeight); 349 itemRect.left = itemRect.top = 0; 350 itemRect.bottom = fontHeight.ascent + fontHeight.descent + 4; 351 itemRect.right = menuFont.StringWidth("99999x99999") + 16; 352 rows = modeCount / 3 + 1; 353 } 354 355 index = 0; 356 for (int32 i = 0; i < fullModeCount; i++) { 357 screen_mode mode = fScreenMode.ModeAt(i); 358 359 if (mode.width == previousWidth && mode.height == previousHeight) 360 continue; 361 362 previousWidth = mode.width; 363 previousHeight = mode.height; 364 365 BMessage* message = new BMessage(POP_RESOLUTION_MSG); 366 message->AddInt32("width", mode.width); 367 message->AddInt32("height", mode.height); 368 369 BString name; 370 name.SetToFormat(B_TRANSLATE_COMMENT("%" B_PRId32" × %" B_PRId32, 371 "The '×' is the Unicode multiplication sign U+00D7"), 372 mode.width, mode.height); 373 374 if (layout == B_ITEMS_IN_COLUMN) 375 fResolutionMenu->AddItem(new BMenuItem(name.String(), message)); 376 else { 377 int y = index % rows; 378 int x = index / rows; 379 itemRect.OffsetTo(x * itemRect.Width(), y * itemRect.Height()); 380 fResolutionMenu->AddItem(new BMenuItem(name.String(), message), itemRect); 381 } 382 383 index++; 384 } 385 386 fMonitorView->SetMaxResolution(maxWidth, maxHeight); 387 388 fResolutionField = new BMenuField("ResolutionMenu", 389 B_TRANSLATE("Resolution:"), fResolutionMenu); 390 fResolutionField->SetAlignment(B_ALIGN_RIGHT); 391 392 fColorsMenu = new BPopUpMenu("colors", true, false); 393 394 for (int32 i = 0; i < kColorSpaceCount; i++) { 395 if ((fSupportedColorSpaces & (1 << i)) == 0) 396 continue; 397 398 BMessage* message = new BMessage(POP_COLORS_MSG); 399 message->AddInt32("bits_per_pixel", kColorSpaces[i].bits_per_pixel); 400 message->AddInt32("space", kColorSpaces[i].space); 401 402 BMenuItem* item = new BMenuItem(kColorSpaces[i].label, message); 403 if (kColorSpaces[i].space == screen.ColorSpace()) 404 fUserSelectedColorSpace = item; 405 406 fColorsMenu->AddItem(item); 407 } 408 409 fColorsField = new BMenuField("ColorsMenu", B_TRANSLATE("Colors:"), 410 fColorsMenu); 411 fColorsField->SetAlignment(B_ALIGN_RIGHT); 412 413 fRefreshMenu = new BPopUpMenu("refresh rate", true, true); 414 415 float min, max; 416 if (fScreenMode.GetRefreshLimits(fActive, min, max) != B_OK) { 417 // if we couldn't obtain the refresh limits, reset to the default 418 // range. Constraints from detected monitors will fine-tune this 419 // later. 420 min = kRefreshRates[0]; 421 max = kRefreshRates[kRefreshRateCount - 1]; 422 } 423 424 if (min == max) { 425 // This is a special case for drivers that only support a single 426 // frequency, like the VESA driver 427 BString name; 428 refresh_rate_to_string(min, name); 429 BMessage *message = new BMessage(POP_REFRESH_MSG); 430 message->AddFloat("refresh", min); 431 BMenuItem *item = new BMenuItem(name.String(), message); 432 fRefreshMenu->AddItem(item); 433 item->SetEnabled(false); 434 } else { 435 monitor_info info; 436 if (fScreenMode.GetMonitorInfo(info) == B_OK) { 437 min = max_c(info.min_vertical_frequency, min); 438 max = min_c(info.max_vertical_frequency, max); 439 } 440 441 for (int32 i = 0; i < kRefreshRateCount; ++i) { 442 if (kRefreshRates[i] < min || kRefreshRates[i] > max) 443 continue; 444 445 BString name; 446 name << kRefreshRates[i] << " " << B_TRANSLATE("Hz"); 447 448 BMessage *message = new BMessage(POP_REFRESH_MSG); 449 message->AddFloat("refresh", kRefreshRates[i]); 450 451 fRefreshMenu->AddItem(new BMenuItem(name.String(), message)); 452 } 453 454 fOtherRefresh = new BMenuItem(B_TRANSLATE("Other" B_UTF8_ELLIPSIS), 455 new BMessage(POP_OTHER_REFRESH_MSG)); 456 fRefreshMenu->AddItem(fOtherRefresh); 457 } 458 459 fRefreshField = new BMenuField("RefreshMenu", B_TRANSLATE("Refresh rate:"), 460 fRefreshMenu); 461 fRefreshField->SetAlignment(B_ALIGN_RIGHT); 462 463 if (_IsVesa()) 464 fRefreshField->Hide(); 465 466 // enlarged area for multi-monitor settings 467 { 468 bool dummy; 469 uint32 dummy32; 470 bool multiMonSupport; 471 bool useLaptopPanelSupport; 472 bool tvStandardSupport; 473 474 multiMonSupport = TestMultiMonSupport(&screen) == B_OK; 475 useLaptopPanelSupport = GetUseLaptopPanel(&screen, &dummy) == B_OK; 476 tvStandardSupport = GetTVStandard(&screen, &dummy32) == B_OK; 477 478 // even if there is no support, we still create all controls 479 // to make sure we don't access NULL pointers later on 480 481 fCombineMenu = new BPopUpMenu("CombineDisplays", 482 true, true); 483 484 for (int32 i = 0; i < kCombineModeCount; i++) { 485 BMessage *message = new BMessage(POP_COMBINE_DISPLAYS_MSG); 486 message->AddInt32("mode", kCombineModes[i].mode); 487 488 fCombineMenu->AddItem(new BMenuItem(kCombineModes[i].name, 489 message)); 490 } 491 492 fCombineField = new BMenuField("CombineMenu", 493 B_TRANSLATE("Combine displays:"), fCombineMenu); 494 fCombineField->SetAlignment(B_ALIGN_RIGHT); 495 496 if (!multiMonSupport) 497 fCombineField->Hide(); 498 499 fSwapDisplaysMenu = new BPopUpMenu("SwapDisplays", 500 true, true); 501 502 // !order is important - we rely that boolean value == idx 503 BMessage *message = new BMessage(POP_SWAP_DISPLAYS_MSG); 504 message->AddBool("swap", false); 505 fSwapDisplaysMenu->AddItem(new BMenuItem(B_TRANSLATE("no"), message)); 506 507 message = new BMessage(POP_SWAP_DISPLAYS_MSG); 508 message->AddBool("swap", true); 509 fSwapDisplaysMenu->AddItem(new BMenuItem(B_TRANSLATE("yes"), message)); 510 511 fSwapDisplaysField = new BMenuField("SwapMenu", 512 B_TRANSLATE("Swap displays:"), fSwapDisplaysMenu); 513 fSwapDisplaysField->SetAlignment(B_ALIGN_RIGHT); 514 515 if (!multiMonSupport) 516 fSwapDisplaysField->Hide(); 517 518 fUseLaptopPanelMenu = new BPopUpMenu("UseLaptopPanel", 519 true, true); 520 521 // !order is important - we rely that boolean value == idx 522 message = new BMessage(POP_USE_LAPTOP_PANEL_MSG); 523 message->AddBool("use", false); 524 fUseLaptopPanelMenu->AddItem(new BMenuItem(B_TRANSLATE("if needed"), 525 message)); 526 527 message = new BMessage(POP_USE_LAPTOP_PANEL_MSG); 528 message->AddBool("use", true); 529 fUseLaptopPanelMenu->AddItem(new BMenuItem(B_TRANSLATE("always"), 530 message)); 531 532 fUseLaptopPanelField = new BMenuField("UseLaptopPanel", 533 B_TRANSLATE("Use laptop panel:"), fUseLaptopPanelMenu); 534 fUseLaptopPanelField->SetAlignment(B_ALIGN_RIGHT); 535 536 if (!useLaptopPanelSupport) 537 fUseLaptopPanelField->Hide(); 538 539 fTVStandardMenu = new BPopUpMenu("TVStandard", true, true); 540 541 // arbitrary limit 542 uint32 i; 543 for (i = 0; i < 100; ++i) { 544 uint32 mode; 545 if (GetNthSupportedTVStandard(&screen, i, &mode) != B_OK) 546 break; 547 548 BString name = tv_standard_to_string(mode); 549 550 message = new BMessage(POP_TV_STANDARD_MSG); 551 message->AddInt32("tv_standard", mode); 552 553 fTVStandardMenu->AddItem(new BMenuItem(name.String(), message)); 554 } 555 556 fTVStandardField = new BMenuField("tv standard", 557 B_TRANSLATE("Video format:"), fTVStandardMenu); 558 fTVStandardField->SetAlignment(B_ALIGN_RIGHT); 559 560 if (!tvStandardSupport || i == 0) 561 fTVStandardField->Hide(); 562 } 563 564 BLayoutBuilder::Group<>(outerControlsView) 565 .AddGrid(B_USE_DEFAULT_SPACING, B_USE_SMALL_SPACING) 566 .Add(fResolutionField->CreateLabelLayoutItem(), 0, 0) 567 .Add(fResolutionField->CreateMenuBarLayoutItem(), 1, 0) 568 .Add(fColorsField->CreateLabelLayoutItem(), 0, 1) 569 .Add(fColorsField->CreateMenuBarLayoutItem(), 1, 1) 570 .Add(fRefreshField->CreateLabelLayoutItem(), 0, 2) 571 .Add(fRefreshField->CreateMenuBarLayoutItem(), 1, 2) 572 .Add(fCombineField->CreateLabelLayoutItem(), 0, 3) 573 .Add(fCombineField->CreateMenuBarLayoutItem(), 1, 3) 574 .Add(fSwapDisplaysField->CreateLabelLayoutItem(), 0, 4) 575 .Add(fSwapDisplaysField->CreateMenuBarLayoutItem(), 1, 4) 576 .Add(fUseLaptopPanelField->CreateLabelLayoutItem(), 0, 5) 577 .Add(fUseLaptopPanelField->CreateMenuBarLayoutItem(), 1, 5) 578 .Add(fTVStandardField->CreateLabelLayoutItem(), 0, 6) 579 .Add(fTVStandardField->CreateMenuBarLayoutItem(), 1, 6) 580 .End(); 581 582 // TODO: we don't support getting the screen's preferred settings 583 /* fDefaultsButton = new BButton(buttonRect, "DefaultsButton", "Defaults", 584 new BMessage(BUTTON_DEFAULTS_MSG));*/ 585 586 fApplyButton = new BButton("ApplyButton", B_TRANSLATE("Apply"), 587 new BMessage(BUTTON_APPLY_MSG)); 588 fApplyButton->SetEnabled(false); 589 BLayoutBuilder::Group<>(outerControlsView) 590 .AddGlue() 591 .AddGroup(B_HORIZONTAL) 592 .AddGlue() 593 .Add(fApplyButton); 594 595 fRevertButton = new BButton("RevertButton", B_TRANSLATE("Revert"), 596 new BMessage(BUTTON_REVERT_MSG)); 597 fRevertButton->SetEnabled(false); 598 599 BLayoutBuilder::Group<>(this, B_VERTICAL, B_USE_DEFAULT_SPACING) 600 .AddGroup(B_HORIZONTAL) 601 .AddGroup(B_VERTICAL, 0, 1) 602 .AddStrut(floorf(controlsBox->TopBorderOffset() 603 - fScreenBox->TopBorderOffset())) 604 .Add(fScreenBox) 605 .Add(workspacesView) 606 .End() 607 .AddGroup(B_VERTICAL, 0, 1) 608 .Add(controlsBox, 2) 609 .End() 610 .End() 611 .AddGroup(B_HORIZONTAL, B_USE_DEFAULT_SPACING) 612 .Add(fRevertButton) 613 .AddGlue() 614 .End() 615 .SetInsets(B_USE_WINDOW_SPACING); 616 617 _UpdateControls(); 618 _UpdateMonitor(); 619 620 MoveOnScreen(); 621 } 622 623 624 ScreenWindow::~ScreenWindow() 625 { 626 delete fSettings; 627 } 628 629 630 bool 631 ScreenWindow::QuitRequested() 632 { 633 fSettings->SetWindowFrame(Frame()); 634 635 // Write mode of workspace 0 (the boot workspace) to the vesa settings file 636 screen_mode vesaMode; 637 if (fBootWorkspaceApplied && fScreenMode.Get(vesaMode, 0) == B_OK) { 638 status_t status = _WriteVesaModeFile(vesaMode); 639 if (status < B_OK) { 640 BString warning = B_TRANSLATE("Could not write VESA mode settings" 641 " file:\n\t"); 642 warning << strerror(status); 643 BAlert* alert = new BAlert(B_TRANSLATE("Warning"), 644 warning.String(), B_TRANSLATE("OK"), NULL, 645 NULL, B_WIDTH_AS_USUAL, B_WARNING_ALERT); 646 alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE); 647 alert->Go(); 648 } 649 } 650 651 be_app->PostMessage(B_QUIT_REQUESTED); 652 653 return BWindow::QuitRequested(); 654 } 655 656 657 /*! Update resolution list according to combine mode 658 (some resolutions may not be combinable due to memory restrictions). 659 */ 660 void 661 ScreenWindow::_CheckResolutionMenu() 662 { 663 for (int32 i = 0; i < fResolutionMenu->CountItems(); i++) 664 fResolutionMenu->ItemAt(i)->SetEnabled(false); 665 666 for (int32 i = 0; i < fScreenMode.CountModes(); i++) { 667 screen_mode mode = fScreenMode.ModeAt(i); 668 if (mode.combine != fSelected.combine) 669 continue; 670 671 BString name; 672 name.SetToFormat(B_TRANSLATE_COMMENT("%" B_PRId32" × %" B_PRId32, 673 "The '×' is the Unicode multiplication sign U+00D7"), 674 mode.width, mode.height); 675 676 BMenuItem *item = fResolutionMenu->FindItem(name.String()); 677 if (item != NULL) 678 item->SetEnabled(true); 679 } 680 } 681 682 683 /*! Update color and refresh options according to current mode 684 (a color space is made active if there is any mode with 685 given resolution and this colour space; same applies for 686 refresh rate, though "Other…" is always possible) 687 */ 688 void 689 ScreenWindow::_CheckColorMenu() 690 { 691 int32 supportsAnything = false; 692 int32 index = 0; 693 694 for (int32 i = 0; i < kColorSpaceCount; i++) { 695 if ((fSupportedColorSpaces & (1 << i)) == 0) 696 continue; 697 698 bool supported = false; 699 700 for (int32 j = 0; j < fScreenMode.CountModes(); j++) { 701 screen_mode mode = fScreenMode.ModeAt(j); 702 703 if (fSelected.width == mode.width 704 && fSelected.height == mode.height 705 && kColorSpaces[i].space == mode.space 706 && fSelected.combine == mode.combine) { 707 supportsAnything = true; 708 supported = true; 709 break; 710 } 711 } 712 713 BMenuItem* item = fColorsMenu->ItemAt(index++); 714 if (item) 715 item->SetEnabled(supported); 716 } 717 718 fColorsField->SetEnabled(supportsAnything); 719 720 if (!supportsAnything) 721 return; 722 723 // Make sure a valid item is selected 724 725 BMenuItem* item = fColorsMenu->FindMarked(); 726 bool changed = false; 727 728 if (item != fUserSelectedColorSpace) { 729 if (fUserSelectedColorSpace != NULL 730 && fUserSelectedColorSpace->IsEnabled()) { 731 fUserSelectedColorSpace->SetMarked(true); 732 item = fUserSelectedColorSpace; 733 changed = true; 734 } 735 } 736 if (item != NULL && !item->IsEnabled()) { 737 // find the next best item 738 int32 index = fColorsMenu->IndexOf(item); 739 bool found = false; 740 741 for (int32 i = index + 1; i < fColorsMenu->CountItems(); i++) { 742 item = fColorsMenu->ItemAt(i); 743 if (item->IsEnabled()) { 744 found = true; 745 break; 746 } 747 } 748 if (!found) { 749 // search backwards as well 750 for (int32 i = index - 1; i >= 0; i--) { 751 item = fColorsMenu->ItemAt(i); 752 if (item->IsEnabled()) 753 break; 754 } 755 } 756 757 item->SetMarked(true); 758 changed = true; 759 } 760 761 if (changed) { 762 // Update selected space 763 764 BMessage* message = item->Message(); 765 int32 space; 766 if (message->FindInt32("space", &space) == B_OK) { 767 fSelected.space = (color_space)space; 768 _UpdateColorLabel(); 769 } 770 } 771 } 772 773 774 /*! Enable/disable refresh options according to current mode. */ 775 void 776 ScreenWindow::_CheckRefreshMenu() 777 { 778 float min, max; 779 if (fScreenMode.GetRefreshLimits(fSelected, min, max) != B_OK || min == max) 780 return; 781 782 for (int32 i = fRefreshMenu->CountItems(); i-- > 0;) { 783 BMenuItem* item = fRefreshMenu->ItemAt(i); 784 BMessage* message = item->Message(); 785 float refresh; 786 if (message != NULL && message->FindFloat("refresh", &refresh) == B_OK) 787 item->SetEnabled(refresh >= min && refresh <= max); 788 } 789 } 790 791 792 /*! Activate appropriate menu item according to selected refresh rate */ 793 void 794 ScreenWindow::_UpdateRefreshControl() 795 { 796 if (isnan(fSelected.refresh)) { 797 fRefreshMenu->SetEnabled(false); 798 fOtherRefresh->SetLabel(B_TRANSLATE("Unknown")); 799 fOtherRefresh->SetMarked(true); 800 return; 801 } else { 802 fRefreshMenu->SetEnabled(true); 803 } 804 805 for (int32 i = 0; i < fRefreshMenu->CountItems(); i++) { 806 BMenuItem* item = fRefreshMenu->ItemAt(i); 807 if (item->Message()->FindFloat("refresh") == fSelected.refresh) { 808 item->SetMarked(true); 809 // "Other" items only contains a refresh rate when active 810 if (fOtherRefresh != NULL) 811 fOtherRefresh->SetLabel(B_TRANSLATE("Other" B_UTF8_ELLIPSIS)); 812 return; 813 } 814 } 815 816 // this is a non-standard refresh rate 817 if (fOtherRefresh != NULL) { 818 fOtherRefresh->Message()->ReplaceFloat("refresh", fSelected.refresh); 819 fOtherRefresh->SetMarked(true); 820 821 BString string; 822 refresh_rate_to_string(fSelected.refresh, string); 823 fRefreshMenu->Superitem()->SetLabel(string.String()); 824 825 string.Append(B_TRANSLATE("/other" B_UTF8_ELLIPSIS)); 826 fOtherRefresh->SetLabel(string.String()); 827 } 828 } 829 830 831 void 832 ScreenWindow::_UpdateMonitorView() 833 { 834 BMessage updateMessage(UPDATE_DESKTOP_MSG); 835 updateMessage.AddInt32("width", fSelected.width); 836 updateMessage.AddInt32("height", fSelected.height); 837 838 PostMessage(&updateMessage, fMonitorView); 839 } 840 841 842 void 843 ScreenWindow::_UpdateControls() 844 { 845 _UpdateWorkspaceButtons(); 846 847 BMenuItem* item = fSwapDisplaysMenu->ItemAt((int32)fSelected.swap_displays); 848 if (item != NULL && !item->IsMarked()) 849 item->SetMarked(true); 850 851 item = fUseLaptopPanelMenu->ItemAt((int32)fSelected.use_laptop_panel); 852 if (item != NULL && !item->IsMarked()) 853 item->SetMarked(true); 854 855 for (int32 i = 0; i < fTVStandardMenu->CountItems(); i++) { 856 item = fTVStandardMenu->ItemAt(i); 857 858 uint32 tvStandard; 859 item->Message()->FindInt32("tv_standard", (int32 *)&tvStandard); 860 if (tvStandard == fSelected.tv_standard) { 861 if (!item->IsMarked()) 862 item->SetMarked(true); 863 break; 864 } 865 } 866 867 _CheckResolutionMenu(); 868 _CheckColorMenu(); 869 _CheckRefreshMenu(); 870 871 BString string; 872 resolution_to_string(fSelected, string); 873 item = fResolutionMenu->FindItem(string.String()); 874 875 if (item != NULL) { 876 if (!item->IsMarked()) 877 item->SetMarked(true); 878 } else { 879 // this is bad luck - if mode has been set via screen references, 880 // this case cannot occur; there are three possible solutions: 881 // 1. add a new resolution to list 882 // - we had to remove it as soon as a "valid" one is selected 883 // - we don't know which frequencies/bit depths are supported 884 // - as long as we haven't the GMT formula to create 885 // parameters for any resolution given, we cannot 886 // really set current mode - it's just not in the list 887 // 2. choose nearest resolution 888 // - probably a good idea, but implies coding and testing 889 // 3. choose lowest resolution 890 // - do you really think we are so lazy? yes, we are 891 item = fResolutionMenu->ItemAt(0); 892 if (item) 893 item->SetMarked(true); 894 895 // okay - at least we set menu label to active resolution 896 fResolutionMenu->Superitem()->SetLabel(string.String()); 897 } 898 899 // mark active combine mode 900 for (int32 i = 0; i < kCombineModeCount; i++) { 901 if (kCombineModes[i].mode == fSelected.combine) { 902 item = fCombineMenu->ItemAt(i); 903 if (item != NULL && !item->IsMarked()) 904 item->SetMarked(true); 905 break; 906 } 907 } 908 909 item = fColorsMenu->ItemAt(0); 910 911 for (int32 i = 0, index = 0; i < kColorSpaceCount; i++) { 912 if ((fSupportedColorSpaces & (1 << i)) == 0) 913 continue; 914 915 if (kColorSpaces[i].space == fSelected.space) { 916 item = fColorsMenu->ItemAt(index); 917 break; 918 } 919 920 index++; 921 } 922 923 if (item != NULL && !item->IsMarked()) 924 item->SetMarked(true); 925 926 _UpdateColorLabel(); 927 _UpdateMonitorView(); 928 _UpdateRefreshControl(); 929 930 _CheckApplyEnabled(); 931 } 932 933 934 /*! Reflect active mode in chosen settings */ 935 void 936 ScreenWindow::_UpdateActiveMode() 937 { 938 _UpdateActiveMode(current_workspace()); 939 } 940 941 942 void 943 ScreenWindow::_UpdateActiveMode(int32 workspace) 944 { 945 // Usually, this function gets called after a mode 946 // has been set manually; still, as the graphics driver 947 // is free to fiddle with mode passed, we better ask 948 // what kind of mode we actually got 949 if (fScreenMode.Get(fActive, workspace) == B_OK) { 950 fSelected = fActive; 951 952 _UpdateMonitor(); 953 _BuildSupportedColorSpaces(); 954 _UpdateControls(); 955 } 956 } 957 958 959 void 960 ScreenWindow::_UpdateWorkspaceButtons() 961 { 962 uint32 columns; 963 uint32 rows; 964 BPrivate::get_workspaces_layout(&columns, &rows); 965 966 // Set the max values enabling/disabling the up/down arrows 967 968 if (rows == 1) 969 fColumnsControl->SetMaxValue(32); 970 else if (rows == 2) 971 fColumnsControl->SetMaxValue(16); 972 else if (rows <= 4) 973 fColumnsControl->SetMaxValue(8); 974 else if (rows <= 8) 975 fColumnsControl->SetMaxValue(4); 976 else if (rows <= 16) 977 fColumnsControl->SetMaxValue(2); 978 else if (rows <= 32) 979 fColumnsControl->SetMaxValue(1); 980 981 if (columns == 1) 982 fRowsControl->SetMaxValue(32); 983 else if (columns == 2) 984 fRowsControl->SetMaxValue(16); 985 else if (columns <= 4) 986 fRowsControl->SetMaxValue(8); 987 else if (columns <= 8) 988 fRowsControl->SetMaxValue(4); 989 else if (columns <= 16) 990 fRowsControl->SetMaxValue(2); 991 else if (columns <= 32) 992 fRowsControl->SetMaxValue(1); 993 } 994 995 996 void 997 ScreenWindow::ScreenChanged(BRect frame, color_space mode) 998 { 999 // move window on screen, if necessary 1000 if (frame.right <= Frame().right 1001 && frame.bottom <= Frame().bottom) { 1002 MoveTo((frame.Width() - Frame().Width()) / 2, 1003 (frame.Height() - Frame().Height()) / 2); 1004 } 1005 } 1006 1007 1008 void 1009 ScreenWindow::WorkspaceActivated(int32 workspace, bool state) 1010 { 1011 if (fScreenMode.GetOriginalMode(fOriginal, workspace) == B_OK) { 1012 _UpdateActiveMode(workspace); 1013 1014 BMessage message(UPDATE_DESKTOP_COLOR_MSG); 1015 PostMessage(&message, fMonitorView); 1016 } 1017 } 1018 1019 1020 void 1021 ScreenWindow::MessageReceived(BMessage* message) 1022 { 1023 switch (message->what) { 1024 case WORKSPACE_CHECK_MSG: 1025 _CheckApplyEnabled(); 1026 break; 1027 1028 case kMsgWorkspaceColumnsChanged: 1029 { 1030 uint32 newColumns = (uint32)fColumnsControl->Value(); 1031 1032 uint32 rows; 1033 BPrivate::get_workspaces_layout(NULL, &rows); 1034 BPrivate::set_workspaces_layout(newColumns, rows); 1035 1036 _UpdateWorkspaceButtons(); 1037 fRowsControl->SetValue(rows); 1038 // enables/disables up/down arrows 1039 _CheckApplyEnabled(); 1040 1041 break; 1042 } 1043 1044 case kMsgWorkspaceRowsChanged: 1045 { 1046 uint32 newRows = (uint32)fRowsControl->Value(); 1047 1048 uint32 columns; 1049 BPrivate::get_workspaces_layout(&columns, NULL); 1050 BPrivate::set_workspaces_layout(columns, newRows); 1051 1052 _UpdateWorkspaceButtons(); 1053 fColumnsControl->SetValue(columns); 1054 // enables/disables up/down arrows 1055 _CheckApplyEnabled(); 1056 break; 1057 } 1058 1059 case POP_RESOLUTION_MSG: 1060 { 1061 message->FindInt32("width", &fSelected.width); 1062 message->FindInt32("height", &fSelected.height); 1063 1064 _CheckColorMenu(); 1065 _CheckRefreshMenu(); 1066 1067 _UpdateMonitorView(); 1068 _UpdateRefreshControl(); 1069 1070 _CheckApplyEnabled(); 1071 break; 1072 } 1073 1074 case POP_COLORS_MSG: 1075 { 1076 int32 space; 1077 if (message->FindInt32("space", &space) != B_OK) 1078 break; 1079 1080 int32 index; 1081 if (message->FindInt32("index", &index) == B_OK 1082 && fColorsMenu->ItemAt(index) != NULL) 1083 fUserSelectedColorSpace = fColorsMenu->ItemAt(index); 1084 1085 fSelected.space = (color_space)space; 1086 _UpdateColorLabel(); 1087 1088 _CheckApplyEnabled(); 1089 break; 1090 } 1091 1092 case POP_REFRESH_MSG: 1093 { 1094 message->FindFloat("refresh", &fSelected.refresh); 1095 fOtherRefresh->SetLabel(B_TRANSLATE("Other" B_UTF8_ELLIPSIS)); 1096 // revert "Other…" label - it might have a refresh rate prefix 1097 1098 _CheckApplyEnabled(); 1099 break; 1100 } 1101 1102 case POP_OTHER_REFRESH_MSG: 1103 { 1104 // make sure menu shows something useful 1105 _UpdateRefreshControl(); 1106 1107 float min = 0, max = 999; 1108 fScreenMode.GetRefreshLimits(fSelected, min, max); 1109 if (min < gMinRefresh) 1110 min = gMinRefresh; 1111 if (max > gMaxRefresh) 1112 max = gMaxRefresh; 1113 1114 monitor_info info; 1115 if (fScreenMode.GetMonitorInfo(info) == B_OK) { 1116 min = max_c(info.min_vertical_frequency, min); 1117 max = min_c(info.max_vertical_frequency, max); 1118 } 1119 1120 RefreshWindow *fRefreshWindow = new RefreshWindow( 1121 fRefreshField->ConvertToScreen(B_ORIGIN), fSelected.refresh, 1122 min, max); 1123 fRefreshWindow->Show(); 1124 break; 1125 } 1126 1127 case SET_CUSTOM_REFRESH_MSG: 1128 { 1129 // user pressed "done" in "Other…" refresh dialog; 1130 // select the refresh rate chosen 1131 message->FindFloat("refresh", &fSelected.refresh); 1132 1133 _UpdateRefreshControl(); 1134 _CheckApplyEnabled(); 1135 break; 1136 } 1137 1138 case POP_COMBINE_DISPLAYS_MSG: 1139 { 1140 // new combine mode has bee chosen 1141 int32 mode; 1142 if (message->FindInt32("mode", &mode) == B_OK) 1143 fSelected.combine = (combine_mode)mode; 1144 1145 _CheckResolutionMenu(); 1146 _CheckApplyEnabled(); 1147 break; 1148 } 1149 1150 case POP_SWAP_DISPLAYS_MSG: 1151 message->FindBool("swap", &fSelected.swap_displays); 1152 _CheckApplyEnabled(); 1153 break; 1154 1155 case POP_USE_LAPTOP_PANEL_MSG: 1156 message->FindBool("use", &fSelected.use_laptop_panel); 1157 _CheckApplyEnabled(); 1158 break; 1159 1160 case POP_TV_STANDARD_MSG: 1161 message->FindInt32("tv_standard", (int32 *)&fSelected.tv_standard); 1162 _CheckApplyEnabled(); 1163 break; 1164 1165 case BUTTON_LAUNCH_BACKGROUNDS_MSG: 1166 if (be_roster->Launch(kBackgroundsSignature) == B_ALREADY_RUNNING) { 1167 app_info info; 1168 be_roster->GetAppInfo(kBackgroundsSignature, &info); 1169 be_roster->ActivateApp(info.team); 1170 } 1171 break; 1172 1173 case BUTTON_DEFAULTS_MSG: 1174 { 1175 // TODO: get preferred settings of screen 1176 fSelected.width = 640; 1177 fSelected.height = 480; 1178 fSelected.space = B_CMAP8; 1179 fSelected.refresh = 60.0; 1180 fSelected.combine = kCombineDisable; 1181 fSelected.swap_displays = false; 1182 fSelected.use_laptop_panel = false; 1183 fSelected.tv_standard = 0; 1184 1185 // TODO: workspace defaults 1186 1187 _UpdateControls(); 1188 break; 1189 } 1190 1191 case BUTTON_UNDO_MSG: 1192 fUndoScreenMode.Revert(); 1193 _UpdateActiveMode(); 1194 break; 1195 1196 case BUTTON_REVERT_MSG: 1197 { 1198 fModified = false; 1199 fBootWorkspaceApplied = false; 1200 1201 // ScreenMode::Revert() assumes that we first set the correct 1202 // number of workspaces 1203 1204 BPrivate::set_workspaces_layout(fOriginalWorkspacesColumns, 1205 fOriginalWorkspacesRows); 1206 _UpdateWorkspaceButtons(); 1207 1208 fScreenMode.Revert(); 1209 1210 BScreen screen(this); 1211 screen.SetBrightness(fOriginalBrightness); 1212 fBrightnessSlider->SetValue(fOriginalBrightness * 255); 1213 1214 _UpdateActiveMode(); 1215 break; 1216 } 1217 1218 case BUTTON_APPLY_MSG: 1219 _Apply(); 1220 break; 1221 1222 case MAKE_INITIAL_MSG: 1223 // user pressed "keep" in confirmation dialog 1224 fModified = true; 1225 _UpdateActiveMode(); 1226 break; 1227 1228 case UPDATE_DESKTOP_COLOR_MSG: 1229 PostMessage(message, fMonitorView); 1230 break; 1231 1232 case SLIDER_BRIGHTNESS_MSG: 1233 { 1234 BScreen screen(this); 1235 screen.SetBrightness(message->FindInt32("be:value") / 255.f); 1236 _CheckApplyEnabled(); 1237 break; 1238 } 1239 1240 default: 1241 BWindow::MessageReceived(message); 1242 } 1243 } 1244 1245 1246 status_t 1247 ScreenWindow::_WriteVesaModeFile(const screen_mode& mode) const 1248 { 1249 BPath path; 1250 status_t status = find_directory(B_USER_SETTINGS_DIRECTORY, &path, true); 1251 if (status < B_OK) 1252 return status; 1253 1254 path.Append("kernel/drivers"); 1255 status = create_directory(path.Path(), 0755); 1256 if (status < B_OK) 1257 return status; 1258 1259 path.Append("vesa"); 1260 BFile file; 1261 status = file.SetTo(path.Path(), B_CREATE_FILE | B_WRITE_ONLY | B_ERASE_FILE); 1262 if (status < B_OK) 1263 return status; 1264 1265 char buffer[256]; 1266 snprintf(buffer, sizeof(buffer), "mode %" B_PRId32 " %" B_PRId32 " %" 1267 B_PRId32 "\n", mode.width, mode.height, mode.BitsPerPixel()); 1268 1269 ssize_t bytesWritten = file.Write(buffer, strlen(buffer)); 1270 if (bytesWritten < B_OK) 1271 return bytesWritten; 1272 1273 return B_OK; 1274 } 1275 1276 1277 void 1278 ScreenWindow::_BuildSupportedColorSpaces() 1279 { 1280 fSupportedColorSpaces = 0; 1281 1282 for (int32 i = 0; i < kColorSpaceCount; i++) { 1283 for (int32 j = 0; j < fScreenMode.CountModes(); j++) { 1284 if (fScreenMode.ModeAt(j).space == kColorSpaces[i].space) { 1285 fSupportedColorSpaces |= 1 << i; 1286 break; 1287 } 1288 } 1289 } 1290 } 1291 1292 1293 void 1294 ScreenWindow::_CheckApplyEnabled() 1295 { 1296 bool applyEnabled = true; 1297 1298 if (fSelected == fActive) { 1299 applyEnabled = false; 1300 if (fAllWorkspacesItem->IsMarked()) { 1301 screen_mode screenMode; 1302 const int32 workspaceCount = count_workspaces(); 1303 for (int32 i = 0; i < workspaceCount; i++) { 1304 fScreenMode.Get(screenMode, i); 1305 if (screenMode != fSelected) { 1306 applyEnabled = true; 1307 break; 1308 } 1309 } 1310 } 1311 } 1312 1313 fApplyButton->SetEnabled(applyEnabled); 1314 1315 uint32 columns; 1316 uint32 rows; 1317 BPrivate::get_workspaces_layout(&columns, &rows); 1318 1319 BScreen screen(this); 1320 float brightness = -1; 1321 screen.GetBrightness(&brightness); 1322 1323 fRevertButton->SetEnabled(columns != fOriginalWorkspacesColumns 1324 || rows != fOriginalWorkspacesRows 1325 || brightness != fOriginalBrightness 1326 || fSelected != fOriginal); 1327 } 1328 1329 1330 void 1331 ScreenWindow::_UpdateOriginal() 1332 { 1333 BPrivate::get_workspaces_layout(&fOriginalWorkspacesColumns, 1334 &fOriginalWorkspacesRows); 1335 1336 fScreenMode.Get(fOriginal); 1337 fScreenMode.UpdateOriginalModes(); 1338 } 1339 1340 1341 void 1342 ScreenWindow::_UpdateMonitor() 1343 { 1344 monitor_info info; 1345 float diagonalInches; 1346 status_t status = fScreenMode.GetMonitorInfo(info, &diagonalInches); 1347 if (status == B_OK) { 1348 char text[512]; 1349 snprintf(text, sizeof(text), "%s%s%s %g\"", info.vendor, 1350 info.name[0] ? " " : "", info.name, diagonalInches); 1351 1352 fScreenBox->SetLabel(text); 1353 } else { 1354 fScreenBox->SetLabel(B_TRANSLATE("Display info")); 1355 } 1356 1357 // Add info about the graphics device 1358 1359 accelerant_device_info deviceInfo; 1360 1361 if (fScreenMode.GetDeviceInfo(deviceInfo) == B_OK) { 1362 BString deviceString; 1363 1364 if (deviceInfo.name[0] && deviceInfo.chipset[0]) { 1365 deviceString.SetToFormat("%s (%s)", deviceInfo.name, 1366 deviceInfo.chipset); 1367 } else if (deviceInfo.name[0] || deviceInfo.chipset[0]) { 1368 deviceString 1369 = deviceInfo.name[0] ? deviceInfo.name : deviceInfo.chipset; 1370 } 1371 1372 fDeviceInfo->SetText(deviceString); 1373 } 1374 1375 1376 char text[512]; 1377 size_t length = 0; 1378 text[0] = 0; 1379 1380 if (status == B_OK) { 1381 if (info.min_horizontal_frequency != 0 1382 && info.min_vertical_frequency != 0 1383 && info.max_pixel_clock != 0) { 1384 length = snprintf(text, sizeof(text), 1385 B_TRANSLATE("Horizonal frequency:\t%lu - %lu kHz\n" 1386 "Vertical frequency:\t%lu - %lu Hz\n\n" 1387 "Maximum pixel clock:\t%g MHz"), 1388 (long unsigned)info.min_horizontal_frequency, 1389 (long unsigned)info.max_horizontal_frequency, 1390 (long unsigned)info.min_vertical_frequency, 1391 (long unsigned)info.max_vertical_frequency, 1392 info.max_pixel_clock / 1000.0); 1393 } 1394 if (info.serial_number[0] && length < sizeof(text)) { 1395 if (length > 0) { 1396 text[length++] = '\n'; 1397 text[length++] = '\n'; 1398 text[length] = '\0'; 1399 } 1400 length += snprintf(text + length, sizeof(text) - length, 1401 B_TRANSLATE("Serial no.: %s"), info.serial_number); 1402 if (info.produced.week != 0 && info.produced.year != 0 1403 && length < sizeof(text)) { 1404 length += snprintf(text + length, sizeof(text) - length, 1405 " (%u/%u)", info.produced.week, info.produced.year); 1406 } 1407 } 1408 } 1409 1410 if (text[0]) 1411 fMonitorView->SetToolTip(text); 1412 } 1413 1414 1415 void 1416 ScreenWindow::_UpdateColorLabel() 1417 { 1418 BString string; 1419 string << fSelected.BitsPerPixel() << " " << B_TRANSLATE("bits/pixel"); 1420 fColorsMenu->Superitem()->SetLabel(string.String()); 1421 } 1422 1423 1424 void 1425 ScreenWindow::_Apply() 1426 { 1427 // make checkpoint, so we can undo these changes 1428 fUndoScreenMode.UpdateOriginalModes(); 1429 1430 status_t status = fScreenMode.Set(fSelected); 1431 if (status == B_OK) { 1432 // use the mode that has eventually been set and 1433 // thus we know to be working; it can differ from 1434 // the mode selected by user due to hardware limitation 1435 display_mode newMode; 1436 BScreen screen(this); 1437 screen.GetMode(&newMode); 1438 1439 if (fAllWorkspacesItem->IsMarked()) { 1440 int32 originatingWorkspace = current_workspace(); 1441 const int32 workspaceCount = count_workspaces(); 1442 for (int32 i = 0; i < workspaceCount; i++) { 1443 if (i != originatingWorkspace) 1444 screen.SetMode(i, &newMode, true); 1445 } 1446 fBootWorkspaceApplied = true; 1447 } else { 1448 if (current_workspace() == 0) 1449 fBootWorkspaceApplied = true; 1450 } 1451 1452 fActive = fSelected; 1453 1454 // TODO: only show alert when this is an unknown mode 1455 BAlert* window = new AlertWindow(this); 1456 window->Go(NULL); 1457 } else { 1458 char message[256]; 1459 snprintf(message, sizeof(message), 1460 B_TRANSLATE("The screen mode could not be set:\n\t%s\n"), 1461 screen_errors(status)); 1462 BAlert* alert = new BAlert(B_TRANSLATE("Warning"), message, 1463 B_TRANSLATE("OK"), NULL, NULL, 1464 B_WIDTH_AS_USUAL, B_WARNING_ALERT); 1465 alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE); 1466 alert->Go(); 1467 } 1468 } 1469