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