2 * Copyright 2001-2015 Haiku, Inc. All rights reserved.
3 * Distributed under the terms of the MIT License.
6 * Stephan Aßmus, superstippi@gmx.de
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
14 * John Scipione, jscipione@gmail.com
18 #include "ScreenWindow.h"
25 #include <Application.h>
29 #include <ControlLook.h>
30 #include <Directory.h>
32 #include <FindDirectory.h>
33 #include <InterfaceDefs.h>
34 #include <LayoutBuilder.h>
37 #include <MenuField.h>
38 #include <Messenger.h>
40 #include <PopUpMenu.h>
42 #include <SpaceLayoutItem.h>
45 #include <StringView.h>
49 #include <InterfacePrivate.h>
51 #include "AlertWindow.h"
52 #include "Constants.h"
53 #include "RefreshWindow.h"
54 #include "MonitorView.h"
55 #include "ScreenSettings.h"
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
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
64 * It will be replaced as soon as we introduce an updated accelerant interface
65 * which may even happen before R1 hits the streets.
67 #include "multimon.h" // the usual: DANGER WILL, ROBINSON!
70 #undef B_TRANSLATION_CONTEXT
71 #define B_TRANSLATION_CONTEXT "Screen"
74 const char* kBackgroundsSignature
= "application/x-vnd.Haiku-Backgrounds";
76 // list of officially supported colour spaces
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") }
88 static const int32 kColorSpaceCount
89 = sizeof(kColorSpaces
) / sizeof(kColorSpaces
[0]);
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]);
96 // list of combine modes
100 } kCombineModes
[] = {
101 { kCombineDisable
, B_TRANSLATE("disable") },
102 { kCombineHorizontally
, B_TRANSLATE("horizontally") },
103 { kCombineVertically
, B_TRANSLATE("vertically") }
105 static const int32 kCombineModeCount
106 = sizeof(kCombineModes
) / sizeof(kCombineModes
[0]);
110 tv_standard_to_string(uint32 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";
126 name
<< "??? (" << mode
<< ")";
135 resolution_to_string(screen_mode
& mode
, BString
&string
)
137 string
<< mode
.width
<< " x " << mode
.height
;
142 refresh_rate_to_string(float refresh
, BString
&string
,
143 bool appendUnit
= true, bool alwaysWithFraction
= false)
145 snprintf(string
.LockBuffer(32), 32, "%.*g", refresh
>= 100.0 ? 4 : 3,
147 string
.UnlockBuffer();
150 string
<< " " << B_TRANSLATE("Hz");
155 screen_errors(status_t status
)
158 case B_ENTRY_NOT_FOUND
:
159 return B_TRANSLATE("Unknown mode");
163 return strerror(status
);
168 // #pragma mark - ScreenWindow
171 ScreenWindow::ScreenWindow(ScreenSettings
* settings
)
173 BWindow(settings
->WindowFrame(), B_TRANSLATE_SYSTEM_NAME("Screen"),
174 B_TITLED_WINDOW
, B_NOT_RESIZABLE
| B_NOT_ZOOMABLE
175 | B_AUTO_UPDATE_SIZE_LIMITS
, B_ALL_WORKSPACES
),
177 fBootWorkspaceApplied(false),
180 fUndoScreenMode(this),
183 BScreen
screen(this);
185 accelerant_device_info info
;
186 if (screen
.GetDeviceInfo(&info
) == B_OK
187 && !strcasecmp(info
.chipset
, "VESA"))
191 _BuildSupportedColorSpaces();
192 fActive
= fSelected
= fOriginal
;
194 fSettings
= settings
;
196 // we need the "Current Workspace" first to get its height
198 BPopUpMenu
* popUpMenu
= new BPopUpMenu(B_TRANSLATE("Current workspace"),
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
));
206 popUpMenu
->AddItem(item
);
207 fAllWorkspacesItem
->SetMarked(true);
209 BMenuField
* workspaceMenuField
= new BMenuField("WorkspaceMenu", NULL
,
211 workspaceMenuField
->ResizeToPreferred();
213 // box on the left with workspace count and monitor view
215 BBox
* screenBox
= new BBox("screen box");
216 BGroupLayout
* layout
= new BGroupLayout(B_VERTICAL
, B_USE_SMALL_SPACING
);
217 layout
->SetInsets(B_USE_DEFAULT_SPACING
, B_USE_DEFAULT_SPACING
,
218 B_USE_DEFAULT_SPACING
, B_USE_DEFAULT_SPACING
);
219 screenBox
->SetLayout(layout
);
221 fMonitorInfo
= new BStringView("monitor info", "");
222 fMonitorInfo
->SetAlignment(B_ALIGN_CENTER
);
223 screenBox
->AddChild(fMonitorInfo
);
225 fDeviceInfo
= new BStringView("monitor info", "");
226 fDeviceInfo
->SetAlignment(B_ALIGN_CENTER
);
227 screenBox
->AddChild(fDeviceInfo
);
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 screenBox
->AddChild(fMonitorView
);
235 BStringView
* workspaces
= new BStringView("workspaces",
236 B_TRANSLATE("Workspaces"));
237 workspaces
->SetAlignment(B_ALIGN_CENTER
);
239 fColumnsControl
= new BSpinner("columns", B_TRANSLATE("Columns:"),
240 new BMessage(kMsgWorkspaceColumnsChanged
));
241 fColumnsControl
->SetAlignment(B_ALIGN_RIGHT
);
242 fColumnsControl
->SetRange(1, 32);
244 fRowsControl
= new BSpinner("rows", B_TRANSLATE("Rows:"),
245 new BMessage(kMsgWorkspaceRowsChanged
));
246 fRowsControl
->SetAlignment(B_ALIGN_RIGHT
);
247 fRowsControl
->SetRange(1, 32);
251 BPrivate::get_workspaces_layout(&columns
, &rows
);
252 fColumnsControl
->SetValue(columns
);
253 fRowsControl
->SetValue(rows
);
255 screenBox
->AddChild(BLayoutBuilder::Group
<>()
256 .AddGroup(B_VERTICAL
, B_USE_SMALL_SPACING
)
258 .AddGrid(B_USE_DEFAULT_SPACING
, B_USE_SMALL_SPACING
)
260 .Add(fColumnsControl
->CreateLabelLayoutItem(), 0, 0)
261 .Add(fColumnsControl
->CreateTextViewLayoutItem(), 1, 0)
263 .Add(fRowsControl
->CreateLabelLayoutItem(), 0, 1)
264 .Add(fRowsControl
->CreateTextViewLayoutItem(), 1, 1)
269 fBackgroundsButton
= new BButton("BackgroundsButton",
270 B_TRANSLATE("Set background" B_UTF8_ELLIPSIS
),
271 new BMessage(BUTTON_LAUNCH_BACKGROUNDS_MSG
));
272 fBackgroundsButton
->SetFontSize(be_plain_font
->Size() * 0.9);
273 screenBox
->AddChild(fBackgroundsButton
);
275 // box on the right with screen resolution, etc.
277 BBox
* controlsBox
= new BBox("controls box");
278 controlsBox
->SetLabel(workspaceMenuField
);
279 BGroupView
* outerControlsView
= new BGroupView(B_VERTICAL
);
280 outerControlsView
->GroupLayout()->SetInsets(B_USE_DEFAULT_SPACING
,
281 B_USE_DEFAULT_SPACING
, B_USE_DEFAULT_SPACING
, B_USE_DEFAULT_SPACING
);
282 controlsBox
->AddChild(outerControlsView
);
284 fResolutionMenu
= new BPopUpMenu("resolution", true, true);
287 uint16 maxHeight
= 0;
288 uint16 previousWidth
= 0;
289 uint16 previousHeight
= 0;
290 for (int32 i
= 0; i
< fScreenMode
.CountModes(); i
++) {
291 screen_mode mode
= fScreenMode
.ModeAt(i
);
293 if (mode
.width
== previousWidth
&& mode
.height
== previousHeight
)
296 previousWidth
= mode
.width
;
297 previousHeight
= mode
.height
;
298 if (maxWidth
< mode
.width
)
299 maxWidth
= mode
.width
;
300 if (maxHeight
< mode
.height
)
301 maxHeight
= mode
.height
;
303 BMessage
* message
= new BMessage(POP_RESOLUTION_MSG
);
304 message
->AddInt32("width", mode
.width
);
305 message
->AddInt32("height", mode
.height
);
308 name
<< mode
.width
<< " x " << mode
.height
;
310 fResolutionMenu
->AddItem(new BMenuItem(name
.String(), message
));
313 fMonitorView
->SetMaxResolution(maxWidth
, maxHeight
);
315 fResolutionField
= new BMenuField("ResolutionMenu",
316 B_TRANSLATE("Resolution:"), fResolutionMenu
);
317 fResolutionField
->SetAlignment(B_ALIGN_RIGHT
);
319 fColorsMenu
= new BPopUpMenu("colors", true, false);
321 for (int32 i
= 0; i
< kColorSpaceCount
; i
++) {
322 if ((fSupportedColorSpaces
& (1 << i
)) == 0)
325 BMessage
* message
= new BMessage(POP_COLORS_MSG
);
326 message
->AddInt32("bits_per_pixel", kColorSpaces
[i
].bits_per_pixel
);
327 message
->AddInt32("space", kColorSpaces
[i
].space
);
329 BMenuItem
* item
= new BMenuItem(kColorSpaces
[i
].label
, message
);
330 if (kColorSpaces
[i
].space
== screen
.ColorSpace())
331 fUserSelectedColorSpace
= item
;
333 fColorsMenu
->AddItem(item
);
336 fColorsField
= new BMenuField("ColorsMenu", B_TRANSLATE("Colors:"),
338 fColorsField
->SetAlignment(B_ALIGN_RIGHT
);
340 fRefreshMenu
= new BPopUpMenu("refresh rate", true, true);
343 if (fScreenMode
.GetRefreshLimits(fActive
, min
, max
) != B_OK
) {
344 // if we couldn't obtain the refresh limits, reset to the default
345 // range. Constraints from detected monitors will fine-tune this
347 min
= kRefreshRates
[0];
348 max
= kRefreshRates
[kRefreshRateCount
- 1];
352 // This is a special case for drivers that only support a single
353 // frequency, like the VESA driver
355 refresh_rate_to_string(min
, name
);
356 BMessage
*message
= new BMessage(POP_REFRESH_MSG
);
357 message
->AddFloat("refresh", min
);
358 BMenuItem
*item
= new BMenuItem(name
.String(), message
);
359 fRefreshMenu
->AddItem(item
);
360 item
->SetEnabled(false);
363 if (fScreenMode
.GetMonitorInfo(info
) == B_OK
) {
364 min
= max_c(info
.min_vertical_frequency
, min
);
365 max
= min_c(info
.max_vertical_frequency
, max
);
368 for (int32 i
= 0; i
< kRefreshRateCount
; ++i
) {
369 if (kRefreshRates
[i
] < min
|| kRefreshRates
[i
] > max
)
373 name
<< kRefreshRates
[i
] << " " << B_TRANSLATE("Hz");
375 BMessage
*message
= new BMessage(POP_REFRESH_MSG
);
376 message
->AddFloat("refresh", kRefreshRates
[i
]);
378 fRefreshMenu
->AddItem(new BMenuItem(name
.String(), message
));
381 fOtherRefresh
= new BMenuItem(B_TRANSLATE("Other" B_UTF8_ELLIPSIS
),
382 new BMessage(POP_OTHER_REFRESH_MSG
));
383 fRefreshMenu
->AddItem(fOtherRefresh
);
386 fRefreshField
= new BMenuField("RefreshMenu", B_TRANSLATE("Refresh rate:"),
388 fRefreshField
->SetAlignment(B_ALIGN_RIGHT
);
391 fRefreshField
->Hide();
393 // enlarged area for multi-monitor settings
397 bool multiMonSupport
;
398 bool useLaptopPanelSupport
;
399 bool tvStandardSupport
;
401 multiMonSupport
= TestMultiMonSupport(&screen
) == B_OK
;
402 useLaptopPanelSupport
= GetUseLaptopPanel(&screen
, &dummy
) == B_OK
;
403 tvStandardSupport
= GetTVStandard(&screen
, &dummy32
) == B_OK
;
405 // even if there is no support, we still create all controls
406 // to make sure we don't access NULL pointers later on
408 fCombineMenu
= new BPopUpMenu("CombineDisplays",
411 for (int32 i
= 0; i
< kCombineModeCount
; i
++) {
412 BMessage
*message
= new BMessage(POP_COMBINE_DISPLAYS_MSG
);
413 message
->AddInt32("mode", kCombineModes
[i
].mode
);
415 fCombineMenu
->AddItem(new BMenuItem(kCombineModes
[i
].name
,
419 fCombineField
= new BMenuField("CombineMenu",
420 B_TRANSLATE("Combine displays:"), fCombineMenu
);
421 fCombineField
->SetAlignment(B_ALIGN_RIGHT
);
423 if (!multiMonSupport
)
424 fCombineField
->Hide();
426 fSwapDisplaysMenu
= new BPopUpMenu("SwapDisplays",
429 // !order is important - we rely that boolean value == idx
430 BMessage
*message
= new BMessage(POP_SWAP_DISPLAYS_MSG
);
431 message
->AddBool("swap", false);
432 fSwapDisplaysMenu
->AddItem(new BMenuItem(B_TRANSLATE("no"), message
));
434 message
= new BMessage(POP_SWAP_DISPLAYS_MSG
);
435 message
->AddBool("swap", true);
436 fSwapDisplaysMenu
->AddItem(new BMenuItem(B_TRANSLATE("yes"), message
));
438 fSwapDisplaysField
= new BMenuField("SwapMenu",
439 B_TRANSLATE("Swap displays:"), fSwapDisplaysMenu
);
440 fSwapDisplaysField
->SetAlignment(B_ALIGN_RIGHT
);
442 if (!multiMonSupport
)
443 fSwapDisplaysField
->Hide();
445 fUseLaptopPanelMenu
= new BPopUpMenu("UseLaptopPanel",
448 // !order is important - we rely that boolean value == idx
449 message
= new BMessage(POP_USE_LAPTOP_PANEL_MSG
);
450 message
->AddBool("use", false);
451 fUseLaptopPanelMenu
->AddItem(new BMenuItem(B_TRANSLATE("if needed"),
454 message
= new BMessage(POP_USE_LAPTOP_PANEL_MSG
);
455 message
->AddBool("use", true);
456 fUseLaptopPanelMenu
->AddItem(new BMenuItem(B_TRANSLATE("always"),
459 fUseLaptopPanelField
= new BMenuField("UseLaptopPanel",
460 B_TRANSLATE("Use laptop panel:"), fUseLaptopPanelMenu
);
461 fUseLaptopPanelField
->SetAlignment(B_ALIGN_RIGHT
);
463 if (!useLaptopPanelSupport
)
464 fUseLaptopPanelField
->Hide();
466 fTVStandardMenu
= new BPopUpMenu("TVStandard", true, true);
470 for (i
= 0; i
< 100; ++i
) {
472 if (GetNthSupportedTVStandard(&screen
, i
, &mode
) != B_OK
)
475 BString name
= tv_standard_to_string(mode
);
477 message
= new BMessage(POP_TV_STANDARD_MSG
);
478 message
->AddInt32("tv_standard", mode
);
480 fTVStandardMenu
->AddItem(new BMenuItem(name
.String(), message
));
483 fTVStandardField
= new BMenuField("tv standard",
484 B_TRANSLATE("Video format:"), fTVStandardMenu
);
485 fTVStandardField
->SetAlignment(B_ALIGN_RIGHT
);
487 if (!tvStandardSupport
|| i
== 0)
488 fTVStandardField
->Hide();
491 BLayoutBuilder::Group
<>(outerControlsView
)
492 .AddGrid(B_USE_DEFAULT_SPACING
, B_USE_SMALL_SPACING
)
493 .Add(fResolutionField
->CreateLabelLayoutItem(), 0, 0)
494 .Add(fResolutionField
->CreateMenuBarLayoutItem(), 1, 0)
495 .Add(fColorsField
->CreateLabelLayoutItem(), 0, 1)
496 .Add(fColorsField
->CreateMenuBarLayoutItem(), 1, 1)
497 .Add(fRefreshField
->CreateLabelLayoutItem(), 0, 2)
498 .Add(fRefreshField
->CreateMenuBarLayoutItem(), 1, 2)
499 .Add(fCombineField
->CreateLabelLayoutItem(), 0, 3)
500 .Add(fCombineField
->CreateMenuBarLayoutItem(), 1, 3)
501 .Add(fSwapDisplaysField
->CreateLabelLayoutItem(), 0, 4)
502 .Add(fSwapDisplaysField
->CreateMenuBarLayoutItem(), 1, 4)
503 .Add(fUseLaptopPanelField
->CreateLabelLayoutItem(), 0, 5)
504 .Add(fUseLaptopPanelField
->CreateMenuBarLayoutItem(), 1, 5)
505 .Add(fTVStandardField
->CreateLabelLayoutItem(), 0, 6)
506 .Add(fTVStandardField
->CreateMenuBarLayoutItem(), 1, 6)
509 fBrightnessSlider
= new BSlider("brightness", "Brightness",
510 NULL
, 0, 255, B_HORIZONTAL
);
512 status_t result
= screen
.GetBrightness(&fOriginalBrightness
);
513 if (result
== B_OK
) {
514 fBrightnessSlider
->SetModificationMessage(
515 new BMessage(SLIDER_BRIGHTNESS_MSG
));
516 fBrightnessSlider
->SetValue(fOriginalBrightness
* 255);
518 // The driver does not support changing the brightness,
519 // so hide the slider
520 fBrightnessSlider
->Hide();
522 fOriginalBrightness
= -1;
525 // TODO: we don't support getting the screen's preferred settings
526 /* fDefaultsButton = new BButton(buttonRect, "DefaultsButton", "Defaults",
527 new BMessage(BUTTON_DEFAULTS_MSG));*/
529 fApplyButton
= new BButton("ApplyButton", B_TRANSLATE("Apply"),
530 new BMessage(BUTTON_APPLY_MSG
));
531 fApplyButton
->SetEnabled(false);
532 BLayoutBuilder::Group
<>(outerControlsView
)
534 .AddGroup(B_HORIZONTAL
)
538 fRevertButton
= new BButton("RevertButton", B_TRANSLATE("Revert"),
539 new BMessage(BUTTON_REVERT_MSG
));
540 fRevertButton
->SetEnabled(false);
542 BLayoutBuilder::Group
<>(this, B_VERTICAL
, B_USE_DEFAULT_SPACING
)
543 .AddGroup(B_HORIZONTAL
)
544 .AddGroup(B_VERTICAL
, 0, 1)
545 .AddStrut(floorf(controlsBox
->TopBorderOffset()) - 1)
548 .AddGroup(B_VERTICAL
, 0, 1)
550 .Add(fBrightnessSlider
)
553 .AddGroup(B_HORIZONTAL
, B_USE_DEFAULT_SPACING
)
557 .SetInsets(B_USE_WINDOW_SPACING
);
566 ScreenWindow::~ScreenWindow()
573 ScreenWindow::QuitRequested()
575 fSettings
->SetWindowFrame(Frame());
577 // Write mode of workspace 0 (the boot workspace) to the vesa settings file
578 screen_mode vesaMode
;
579 if (fBootWorkspaceApplied
&& fScreenMode
.Get(vesaMode
, 0) == B_OK
) {
580 status_t status
= _WriteVesaModeFile(vesaMode
);
582 BString warning
= B_TRANSLATE("Could not write VESA mode settings"
584 warning
<< strerror(status
);
585 BAlert
* alert
= new BAlert(B_TRANSLATE("Warning"),
586 warning
.String(), B_TRANSLATE("OK"), NULL
,
587 NULL
, B_WIDTH_AS_USUAL
, B_WARNING_ALERT
);
588 alert
->SetFlags(alert
->Flags() | B_CLOSE_ON_ESCAPE
);
593 be_app
->PostMessage(B_QUIT_REQUESTED
);
595 return BWindow::QuitRequested();
599 /*! Update resolution list according to combine mode
600 (some resolutions may not be combinable due to memory restrictions).
603 ScreenWindow::_CheckResolutionMenu()
605 for (int32 i
= 0; i
< fResolutionMenu
->CountItems(); i
++)
606 fResolutionMenu
->ItemAt(i
)->SetEnabled(false);
608 for (int32 i
= 0; i
< fScreenMode
.CountModes(); i
++) {
609 screen_mode mode
= fScreenMode
.ModeAt(i
);
610 if (mode
.combine
!= fSelected
.combine
)
614 name
<< mode
.width
<< " x " << mode
.height
;
616 BMenuItem
*item
= fResolutionMenu
->FindItem(name
.String());
618 item
->SetEnabled(true);
623 /*! Update color and refresh options according to current mode
624 (a color space is made active if there is any mode with
625 given resolution and this colour space; same applies for
626 refresh rate, though "Other…" is always possible)
629 ScreenWindow::_CheckColorMenu()
631 int32 supportsAnything
= false;
634 for (int32 i
= 0; i
< kColorSpaceCount
; i
++) {
635 if ((fSupportedColorSpaces
& (1 << i
)) == 0)
638 bool supported
= false;
640 for (int32 j
= 0; j
< fScreenMode
.CountModes(); j
++) {
641 screen_mode mode
= fScreenMode
.ModeAt(j
);
643 if (fSelected
.width
== mode
.width
644 && fSelected
.height
== mode
.height
645 && kColorSpaces
[i
].space
== mode
.space
646 && fSelected
.combine
== mode
.combine
) {
647 supportsAnything
= true;
653 BMenuItem
* item
= fColorsMenu
->ItemAt(index
++);
655 item
->SetEnabled(supported
);
658 fColorsField
->SetEnabled(supportsAnything
);
660 if (!supportsAnything
)
663 // Make sure a valid item is selected
665 BMenuItem
* item
= fColorsMenu
->FindMarked();
666 bool changed
= false;
668 if (item
!= fUserSelectedColorSpace
) {
669 if (fUserSelectedColorSpace
!= NULL
670 && fUserSelectedColorSpace
->IsEnabled()) {
671 fUserSelectedColorSpace
->SetMarked(true);
672 item
= fUserSelectedColorSpace
;
676 if (item
!= NULL
&& !item
->IsEnabled()) {
677 // find the next best item
678 int32 index
= fColorsMenu
->IndexOf(item
);
681 for (int32 i
= index
+ 1; i
< fColorsMenu
->CountItems(); i
++) {
682 item
= fColorsMenu
->ItemAt(i
);
683 if (item
->IsEnabled()) {
689 // search backwards as well
690 for (int32 i
= index
- 1; i
>= 0; i
--) {
691 item
= fColorsMenu
->ItemAt(i
);
692 if (item
->IsEnabled())
697 item
->SetMarked(true);
702 // Update selected space
704 BMessage
* message
= item
->Message();
706 if (message
->FindInt32("space", &space
) == B_OK
) {
707 fSelected
.space
= (color_space
)space
;
714 /*! Enable/disable refresh options according to current mode. */
716 ScreenWindow::_CheckRefreshMenu()
719 if (fScreenMode
.GetRefreshLimits(fSelected
, min
, max
) != B_OK
|| min
== max
)
722 for (int32 i
= fRefreshMenu
->CountItems(); i
-- > 0;) {
723 BMenuItem
* item
= fRefreshMenu
->ItemAt(i
);
724 BMessage
* message
= item
->Message();
726 if (message
!= NULL
&& message
->FindFloat("refresh", &refresh
) == B_OK
)
727 item
->SetEnabled(refresh
>= min
&& refresh
<= max
);
732 /*! Activate appropriate menu item according to selected refresh rate */
734 ScreenWindow::_UpdateRefreshControl()
736 for (int32 i
= 0; i
< fRefreshMenu
->CountItems(); i
++) {
737 BMenuItem
* item
= fRefreshMenu
->ItemAt(i
);
738 if (item
->Message()->FindFloat("refresh") == fSelected
.refresh
) {
739 item
->SetMarked(true);
740 // "Other" items only contains a refresh rate when active
741 fOtherRefresh
->SetLabel(B_TRANSLATE("Other" B_UTF8_ELLIPSIS
));
746 // this is a non-standard refresh rate
747 if (fOtherRefresh
!= NULL
) {
748 fOtherRefresh
->Message()->ReplaceFloat("refresh", fSelected
.refresh
);
749 fOtherRefresh
->SetMarked(true);
752 refresh_rate_to_string(fSelected
.refresh
, string
);
753 fRefreshMenu
->Superitem()->SetLabel(string
.String());
755 string
.Append(B_TRANSLATE("/other" B_UTF8_ELLIPSIS
));
756 fOtherRefresh
->SetLabel(string
.String());
762 ScreenWindow::_UpdateMonitorView()
764 BMessage
updateMessage(UPDATE_DESKTOP_MSG
);
765 updateMessage
.AddInt32("width", fSelected
.width
);
766 updateMessage
.AddInt32("height", fSelected
.height
);
768 PostMessage(&updateMessage
, fMonitorView
);
773 ScreenWindow::_UpdateControls()
775 _UpdateWorkspaceButtons();
777 BMenuItem
* item
= fSwapDisplaysMenu
->ItemAt((int32
)fSelected
.swap_displays
);
778 if (item
!= NULL
&& !item
->IsMarked())
779 item
->SetMarked(true);
781 item
= fUseLaptopPanelMenu
->ItemAt((int32
)fSelected
.use_laptop_panel
);
782 if (item
!= NULL
&& !item
->IsMarked())
783 item
->SetMarked(true);
785 for (int32 i
= 0; i
< fTVStandardMenu
->CountItems(); i
++) {
786 item
= fTVStandardMenu
->ItemAt(i
);
789 item
->Message()->FindInt32("tv_standard", (int32
*)&tvStandard
);
790 if (tvStandard
== fSelected
.tv_standard
) {
791 if (!item
->IsMarked())
792 item
->SetMarked(true);
797 _CheckResolutionMenu();
802 resolution_to_string(fSelected
, string
);
803 item
= fResolutionMenu
->FindItem(string
.String());
806 if (!item
->IsMarked())
807 item
->SetMarked(true);
809 // this is bad luck - if mode has been set via screen references,
810 // this case cannot occur; there are three possible solutions:
811 // 1. add a new resolution to list
812 // - we had to remove it as soon as a "valid" one is selected
813 // - we don't know which frequencies/bit depths are supported
814 // - as long as we haven't the GMT formula to create
815 // parameters for any resolution given, we cannot
816 // really set current mode - it's just not in the list
817 // 2. choose nearest resolution
818 // - probably a good idea, but implies coding and testing
819 // 3. choose lowest resolution
820 // - do you really think we are so lazy? yes, we are
821 item
= fResolutionMenu
->ItemAt(0);
823 item
->SetMarked(true);
825 // okay - at least we set menu label to active resolution
826 fResolutionMenu
->Superitem()->SetLabel(string
.String());
829 // mark active combine mode
830 for (int32 i
= 0; i
< kCombineModeCount
; i
++) {
831 if (kCombineModes
[i
].mode
== fSelected
.combine
) {
832 item
= fCombineMenu
->ItemAt(i
);
833 if (item
!= NULL
&& !item
->IsMarked())
834 item
->SetMarked(true);
839 item
= fColorsMenu
->ItemAt(0);
841 for (int32 i
= 0, index
= 0; i
< kColorSpaceCount
; i
++) {
842 if ((fSupportedColorSpaces
& (1 << i
)) == 0)
845 if (kColorSpaces
[i
].space
== fSelected
.space
) {
846 item
= fColorsMenu
->ItemAt(index
);
853 if (item
!= NULL
&& !item
->IsMarked())
854 item
->SetMarked(true);
857 _UpdateMonitorView();
858 _UpdateRefreshControl();
860 _CheckApplyEnabled();
864 /*! Reflect active mode in chosen settings */
866 ScreenWindow::_UpdateActiveMode()
868 _UpdateActiveMode(current_workspace());
873 ScreenWindow::_UpdateActiveMode(int32 workspace
)
875 // Usually, this function gets called after a mode
876 // has been set manually; still, as the graphics driver
877 // is free to fiddle with mode passed, we better ask
878 // what kind of mode we actually got
879 if (fScreenMode
.Get(fActive
, workspace
) == B_OK
) {
883 _BuildSupportedColorSpaces();
890 ScreenWindow::_UpdateWorkspaceButtons()
894 BPrivate::get_workspaces_layout(&columns
, &rows
);
896 // Set the max values enabling/disabling the up/down arrows
899 fColumnsControl
->SetMaxValue(32);
901 fColumnsControl
->SetMaxValue(16);
903 fColumnsControl
->SetMaxValue(8);
905 fColumnsControl
->SetMaxValue(4);
907 fColumnsControl
->SetMaxValue(2);
909 fColumnsControl
->SetMaxValue(1);
912 fRowsControl
->SetMaxValue(32);
913 else if (columns
== 2)
914 fRowsControl
->SetMaxValue(16);
915 else if (columns
<= 4)
916 fRowsControl
->SetMaxValue(8);
917 else if (columns
<= 8)
918 fRowsControl
->SetMaxValue(4);
919 else if (columns
<= 16)
920 fRowsControl
->SetMaxValue(2);
921 else if (columns
<= 32)
922 fRowsControl
->SetMaxValue(1);
927 ScreenWindow::ScreenChanged(BRect frame
, color_space mode
)
929 // move window on screen, if necessary
930 if (frame
.right
<= Frame().right
931 && frame
.bottom
<= Frame().bottom
) {
932 MoveTo((frame
.Width() - Frame().Width()) / 2,
933 (frame
.Height() - Frame().Height()) / 2);
939 ScreenWindow::WorkspaceActivated(int32 workspace
, bool state
)
941 if (fScreenMode
.GetOriginalMode(fOriginal
, workspace
) == B_OK
) {
942 _UpdateActiveMode(workspace
);
944 BMessage
message(UPDATE_DESKTOP_COLOR_MSG
);
945 PostMessage(&message
, fMonitorView
);
951 ScreenWindow::MessageReceived(BMessage
* message
)
953 switch (message
->what
) {
954 case WORKSPACE_CHECK_MSG
:
955 _CheckApplyEnabled();
958 case kMsgWorkspaceColumnsChanged
:
960 uint32 newColumns
= (uint32
)fColumnsControl
->Value();
963 BPrivate::get_workspaces_layout(NULL
, &rows
);
964 BPrivate::set_workspaces_layout(newColumns
, rows
);
966 _UpdateWorkspaceButtons();
967 fRowsControl
->SetValue(rows
);
968 // enables/disables up/down arrows
969 _CheckApplyEnabled();
974 case kMsgWorkspaceRowsChanged
:
976 uint32 newRows
= (uint32
)fRowsControl
->Value();
979 BPrivate::get_workspaces_layout(&columns
, NULL
);
980 BPrivate::set_workspaces_layout(columns
, newRows
);
982 _UpdateWorkspaceButtons();
983 fColumnsControl
->SetValue(columns
);
984 // enables/disables up/down arrows
985 _CheckApplyEnabled();
989 case POP_RESOLUTION_MSG
:
991 message
->FindInt32("width", &fSelected
.width
);
992 message
->FindInt32("height", &fSelected
.height
);
997 _UpdateMonitorView();
998 _UpdateRefreshControl();
1000 _CheckApplyEnabled();
1004 case POP_COLORS_MSG
:
1007 if (message
->FindInt32("space", &space
) != B_OK
)
1011 if (message
->FindInt32("index", &index
) == B_OK
1012 && fColorsMenu
->ItemAt(index
) != NULL
)
1013 fUserSelectedColorSpace
= fColorsMenu
->ItemAt(index
);
1015 fSelected
.space
= (color_space
)space
;
1016 _UpdateColorLabel();
1018 _CheckApplyEnabled();
1022 case POP_REFRESH_MSG
:
1024 message
->FindFloat("refresh", &fSelected
.refresh
);
1025 fOtherRefresh
->SetLabel(B_TRANSLATE("Other" B_UTF8_ELLIPSIS
));
1026 // revert "Other…" label - it might have a refresh rate prefix
1028 _CheckApplyEnabled();
1032 case POP_OTHER_REFRESH_MSG
:
1034 // make sure menu shows something useful
1035 _UpdateRefreshControl();
1037 float min
= 0, max
= 999;
1038 fScreenMode
.GetRefreshLimits(fSelected
, min
, max
);
1039 if (min
< gMinRefresh
)
1041 if (max
> gMaxRefresh
)
1045 if (fScreenMode
.GetMonitorInfo(info
) == B_OK
) {
1046 min
= max_c(info
.min_vertical_frequency
, min
);
1047 max
= min_c(info
.max_vertical_frequency
, max
);
1050 RefreshWindow
*fRefreshWindow
= new RefreshWindow(
1051 fRefreshField
->ConvertToScreen(B_ORIGIN
), fSelected
.refresh
,
1053 fRefreshWindow
->Show();
1057 case SET_CUSTOM_REFRESH_MSG
:
1059 // user pressed "done" in "Other…" refresh dialog;
1060 // select the refresh rate chosen
1061 message
->FindFloat("refresh", &fSelected
.refresh
);
1063 _UpdateRefreshControl();
1064 _CheckApplyEnabled();
1068 case POP_COMBINE_DISPLAYS_MSG
:
1070 // new combine mode has bee chosen
1072 if (message
->FindInt32("mode", &mode
) == B_OK
)
1073 fSelected
.combine
= (combine_mode
)mode
;
1075 _CheckResolutionMenu();
1076 _CheckApplyEnabled();
1080 case POP_SWAP_DISPLAYS_MSG
:
1081 message
->FindBool("swap", &fSelected
.swap_displays
);
1082 _CheckApplyEnabled();
1085 case POP_USE_LAPTOP_PANEL_MSG
:
1086 message
->FindBool("use", &fSelected
.use_laptop_panel
);
1087 _CheckApplyEnabled();
1090 case POP_TV_STANDARD_MSG
:
1091 message
->FindInt32("tv_standard", (int32
*)&fSelected
.tv_standard
);
1092 _CheckApplyEnabled();
1095 case BUTTON_LAUNCH_BACKGROUNDS_MSG
:
1096 if (be_roster
->Launch(kBackgroundsSignature
) == B_ALREADY_RUNNING
) {
1098 be_roster
->GetAppInfo(kBackgroundsSignature
, &info
);
1099 be_roster
->ActivateApp(info
.team
);
1103 case BUTTON_DEFAULTS_MSG
:
1105 // TODO: get preferred settings of screen
1106 fSelected
.width
= 640;
1107 fSelected
.height
= 480;
1108 fSelected
.space
= B_CMAP8
;
1109 fSelected
.refresh
= 60.0;
1110 fSelected
.combine
= kCombineDisable
;
1111 fSelected
.swap_displays
= false;
1112 fSelected
.use_laptop_panel
= false;
1113 fSelected
.tv_standard
= 0;
1115 // TODO: workspace defaults
1121 case BUTTON_UNDO_MSG
:
1122 fUndoScreenMode
.Revert();
1123 _UpdateActiveMode();
1126 case BUTTON_REVERT_MSG
:
1129 fBootWorkspaceApplied
= false;
1131 // ScreenMode::Revert() assumes that we first set the correct
1132 // number of workspaces
1134 BPrivate::set_workspaces_layout(fOriginalWorkspacesColumns
,
1135 fOriginalWorkspacesRows
);
1136 _UpdateWorkspaceButtons();
1138 fScreenMode
.Revert();
1139 _UpdateActiveMode();
1141 BScreen
screen(this);
1142 screen
.SetBrightness(fOriginalBrightness
);
1143 fBrightnessSlider
->SetValue(fOriginalBrightness
* 255);
1147 case BUTTON_APPLY_MSG
:
1151 case MAKE_INITIAL_MSG
:
1152 // user pressed "keep" in confirmation dialog
1154 _UpdateActiveMode();
1157 case UPDATE_DESKTOP_COLOR_MSG
:
1158 PostMessage(message
, fMonitorView
);
1161 case SLIDER_BRIGHTNESS_MSG
:
1163 BScreen
screen(this);
1164 screen
.SetBrightness(message
->FindInt32("be:value") / 255.f
);
1165 _CheckApplyEnabled();
1170 BWindow::MessageReceived(message
);
1176 ScreenWindow::_WriteVesaModeFile(const screen_mode
& mode
) const
1179 status_t status
= find_directory(B_USER_SETTINGS_DIRECTORY
, &path
, true);
1183 path
.Append("kernel/drivers");
1184 status
= create_directory(path
.Path(), 0755);
1188 path
.Append("vesa");
1190 status
= file
.SetTo(path
.Path(), B_CREATE_FILE
| B_WRITE_ONLY
| B_ERASE_FILE
);
1195 snprintf(buffer
, sizeof(buffer
), "mode %" B_PRId32
" %" B_PRId32
" %"
1196 B_PRId32
"\n", mode
.width
, mode
.height
, mode
.BitsPerPixel());
1198 ssize_t bytesWritten
= file
.Write(buffer
, strlen(buffer
));
1199 if (bytesWritten
< B_OK
)
1200 return bytesWritten
;
1207 ScreenWindow::_BuildSupportedColorSpaces()
1209 fSupportedColorSpaces
= 0;
1211 for (int32 i
= 0; i
< kColorSpaceCount
; i
++) {
1212 for (int32 j
= 0; j
< fScreenMode
.CountModes(); j
++) {
1213 if (fScreenMode
.ModeAt(j
).space
== kColorSpaces
[i
].space
) {
1214 fSupportedColorSpaces
|= 1 << i
;
1223 ScreenWindow::_CheckApplyEnabled()
1225 bool applyEnabled
= true;
1227 if (fSelected
== fActive
) {
1228 applyEnabled
= false;
1229 if (fAllWorkspacesItem
->IsMarked()) {
1230 screen_mode screenMode
;
1231 const int32 workspaceCount
= count_workspaces();
1232 for (int32 i
= 0; i
< workspaceCount
; i
++) {
1233 fScreenMode
.Get(screenMode
, i
);
1234 if (screenMode
!= fSelected
) {
1235 applyEnabled
= true;
1242 fApplyButton
->SetEnabled(applyEnabled
);
1246 BPrivate::get_workspaces_layout(&columns
, &rows
);
1248 BScreen
screen(this);
1249 float brightness
= -1;
1250 screen
.GetBrightness(&brightness
);
1252 fRevertButton
->SetEnabled(columns
!= fOriginalWorkspacesColumns
1253 || rows
!= fOriginalWorkspacesRows
1254 || brightness
!= fOriginalBrightness
1255 || fSelected
!= fOriginal
);
1260 ScreenWindow::_UpdateOriginal()
1262 BPrivate::get_workspaces_layout(&fOriginalWorkspacesColumns
,
1263 &fOriginalWorkspacesRows
);
1265 fScreenMode
.Get(fOriginal
);
1266 fScreenMode
.UpdateOriginalModes();
1271 ScreenWindow::_UpdateMonitor()
1274 float diagonalInches
;
1275 status_t status
= fScreenMode
.GetMonitorInfo(info
, &diagonalInches
);
1276 if (status
== B_OK
) {
1278 snprintf(text
, sizeof(text
), "%s%s%s %g\"", info
.vendor
,
1279 info
.name
[0] ? " " : "", info
.name
, diagonalInches
);
1281 fMonitorInfo
->SetText(text
);
1283 if (fMonitorInfo
->IsHidden(fMonitorInfo
))
1284 fMonitorInfo
->Show();
1286 if (!fMonitorInfo
->IsHidden(fMonitorInfo
))
1287 fMonitorInfo
->Hide();
1290 // Add info about the graphics device
1292 accelerant_device_info deviceInfo
;
1294 if (fScreenMode
.GetDeviceInfo(deviceInfo
) == B_OK
) {
1295 BString deviceString
;
1297 if (deviceInfo
.name
[0] && deviceInfo
.chipset
[0]) {
1298 deviceString
.SetToFormat("%s (%s)", deviceInfo
.name
,
1299 deviceInfo
.chipset
);
1300 } else if (deviceInfo
.name
[0] || deviceInfo
.chipset
[0]) {
1302 = deviceInfo
.name
[0] ? deviceInfo
.name
: deviceInfo
.chipset
;
1305 fDeviceInfo
->SetText(deviceString
);
1313 if (status
== B_OK
) {
1314 if (info
.min_horizontal_frequency
!= 0
1315 && info
.min_vertical_frequency
!= 0
1316 && info
.max_pixel_clock
!= 0) {
1317 length
= snprintf(text
, sizeof(text
),
1318 B_TRANSLATE("Horizonal frequency:\t%lu - %lu kHz\n"
1319 "Vertical frequency:\t%lu - %lu Hz\n\n"
1320 "Maximum pixel clock:\t%g MHz"),
1321 info
.min_horizontal_frequency
, info
.max_horizontal_frequency
,
1322 info
.min_vertical_frequency
, info
.max_vertical_frequency
,
1323 info
.max_pixel_clock
/ 1000.0);
1325 if (info
.serial_number
[0] && length
< sizeof(text
)) {
1326 length
+= snprintf(text
+ length
, sizeof(text
) - length
,
1327 B_TRANSLATE("%sSerial no.: %s"), length
? "\n\n" : "",
1328 info
.serial_number
);
1329 if (info
.produced
.week
!= 0 && info
.produced
.year
!= 0
1330 && length
< sizeof(text
)) {
1331 length
+= snprintf(text
+ length
, sizeof(text
) - length
,
1332 " (%u/%u)", info
.produced
.week
, info
.produced
.year
);
1338 fMonitorView
->SetToolTip(text
);
1343 ScreenWindow::_UpdateColorLabel()
1346 string
<< fSelected
.BitsPerPixel() << " " << B_TRANSLATE("bits/pixel");
1347 fColorsMenu
->Superitem()->SetLabel(string
.String());
1352 ScreenWindow::_Apply()
1354 // make checkpoint, so we can undo these changes
1355 fUndoScreenMode
.UpdateOriginalModes();
1357 status_t status
= fScreenMode
.Set(fSelected
);
1358 if (status
== B_OK
) {
1359 // use the mode that has eventually been set and
1360 // thus we know to be working; it can differ from
1361 // the mode selected by user due to hardware limitation
1362 display_mode newMode
;
1363 BScreen
screen(this);
1364 screen
.GetMode(&newMode
);
1366 if (fAllWorkspacesItem
->IsMarked()) {
1367 int32 originatingWorkspace
= current_workspace();
1368 const int32 workspaceCount
= count_workspaces();
1369 for (int32 i
= 0; i
< workspaceCount
; i
++) {
1370 if (i
!= originatingWorkspace
)
1371 screen
.SetMode(i
, &newMode
, true);
1373 fBootWorkspaceApplied
= true;
1375 if (current_workspace() == 0)
1376 fBootWorkspaceApplied
= true;
1379 fActive
= fSelected
;
1381 // TODO: only show alert when this is an unknown mode
1382 BAlert
* window
= new AlertWindow(this);
1386 snprintf(message
, sizeof(message
),
1387 B_TRANSLATE("The screen mode could not be set:\n\t%s\n"),
1388 screen_errors(status
));
1389 BAlert
* alert
= new BAlert(B_TRANSLATE("Warning"), message
,
1390 B_TRANSLATE("OK"), NULL
, NULL
,
1391 B_WIDTH_AS_USUAL
, B_WARNING_ALERT
);
1392 alert
->SetFlags(alert
->Flags() | B_CLOSE_ON_ESCAPE
);