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