2 * Copyright 2001-2013, Haiku, Inc.
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>
44 #include <StringView.h>
48 #include <InterfacePrivate.h>
50 #include "AlertWindow.h"
51 #include "Constants.h"
52 #include "RefreshWindow.h"
53 #include "MonitorView.h"
54 #include "ScreenSettings.h"
57 /* Note, this headers defines a *private* interface to the Radeon accelerant.
58 * It's a solution that works with the current BeOS interface that Haiku
60 * However, it's not a nice and clean solution. Don't use this header in any
61 * application if you can avoid it. No other driver is using this, or should
63 * It will be replaced as soon as we introduce an updated accelerant interface
64 * which may even happen before R1 hits the streets.
66 #include "multimon.h" // the usual: DANGER WILL, ROBINSON!
69 #undef B_TRANSLATION_CONTEXT
70 #define B_TRANSLATION_CONTEXT "Screen"
73 const char* kBackgroundsSignature
= "application/x-vnd.Haiku-Backgrounds";
75 // list of officially supported colour spaces
81 { B_CMAP8
, 8, B_TRANSLATE("8 bits/pixel, 256 colors") },
82 { B_RGB15
, 15, B_TRANSLATE("15 bits/pixel, 32768 colors") },
83 { B_RGB16
, 16, B_TRANSLATE("16 bits/pixel, 65536 colors") },
84 { B_RGB24
, 24, B_TRANSLATE("24 bits/pixel, 16 Million colors") },
85 { B_RGB32
, 32, B_TRANSLATE("32 bits/pixel, 16 Million colors") }
87 static const int32 kColorSpaceCount
88 = sizeof(kColorSpaces
) / sizeof(kColorSpaces
[0]);
90 // list of standard refresh rates
91 static const int32 kRefreshRates
[] = { 60, 70, 72, 75, 80, 85, 95, 100 };
92 static const int32 kRefreshRateCount
93 = sizeof(kRefreshRates
) / sizeof(kRefreshRates
[0]);
95 // list of combine modes
100 { kCombineDisable
, B_TRANSLATE("disable") },
101 { kCombineHorizontally
, B_TRANSLATE("horizontally") },
102 { kCombineVertically
, B_TRANSLATE("vertically") }
104 static const int32 kCombineModeCount
105 = sizeof(kCombineModes
) / sizeof(kCombineModes
[0]);
109 tv_standard_to_string(uint32 mode
)
112 case 0: return "disabled";
113 case 1: return "NTSC";
114 case 2: return "NTSC Japan";
115 case 3: return "PAL BDGHI";
116 case 4: return "PAL M";
117 case 5: return "PAL N";
118 case 6: return "SECAM";
119 case 101: return "NTSC 443";
120 case 102: return "PAL 60";
121 case 103: return "PAL NC";
125 name
<< "??? (" << mode
<< ")";
134 resolution_to_string(screen_mode
& mode
, BString
&string
)
136 string
<< mode
.width
<< " x " << mode
.height
;
141 refresh_rate_to_string(float refresh
, BString
&string
,
142 bool appendUnit
= true, bool alwaysWithFraction
= false)
144 snprintf(string
.LockBuffer(32), 32, "%.*g", refresh
>= 100.0 ? 4 : 3,
146 string
.UnlockBuffer();
149 string
<< " " << B_TRANSLATE("Hz");
154 screen_errors(status_t status
)
157 case B_ENTRY_NOT_FOUND
:
158 return B_TRANSLATE("Unknown mode");
162 return strerror(status
);
170 ScreenWindow::ScreenWindow(ScreenSettings
* settings
)
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
),
176 fBootWorkspaceApplied(false),
179 fUndoScreenMode(this),
182 BScreen
screen(this);
184 accelerant_device_info info
;
185 if (screen
.GetDeviceInfo(&info
) == B_OK
186 && !strcasecmp(info
.chipset
, "VESA"))
190 _BuildSupportedColorSpaces();
191 fActive
= fSelected
= fOriginal
;
193 fSettings
= settings
;
195 // we need the "Current Workspace" first to get its height
197 BPopUpMenu
*popUpMenu
= new BPopUpMenu(B_TRANSLATE("Current workspace"),
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
));
205 popUpMenu
->AddItem(item
);
206 fAllWorkspacesItem
->SetMarked(true);
208 BMenuField
* workspaceMenuField
= new BMenuField("WorkspaceMenu", NULL
,
210 workspaceMenuField
->ResizeToPreferred();
212 // box on the left with workspace count and monitor view
214 BBox
* screenBox
= new BBox("screen box");
215 BGroupLayout
* layout
= new BGroupLayout(B_VERTICAL
, B_USE_SMALL_SPACING
);
216 layout
->SetInsets(B_USE_DEFAULT_SPACING
, B_USE_DEFAULT_SPACING
,
217 B_USE_DEFAULT_SPACING
, B_USE_DEFAULT_SPACING
);
218 screenBox
->SetLayout(layout
);
220 fMonitorInfo
= new BStringView("monitor info", "");
221 fMonitorInfo
->SetAlignment(B_ALIGN_CENTER
);
222 screenBox
->AddChild(fMonitorInfo
);
224 fDeviceInfo
= new BStringView("monitor info", "");
225 fDeviceInfo
->SetAlignment(B_ALIGN_CENTER
);
226 screenBox
->AddChild(fDeviceInfo
);
228 fMonitorView
= new MonitorView(BRect(0.0, 0.0, 80.0, 80.0),
229 "monitor", screen
.Frame().IntegerWidth() + 1,
230 screen
.Frame().IntegerHeight() + 1);
231 screenBox
->AddChild(fMonitorView
);
233 BStringView
* workspaces
= new BStringView("workspaces",
234 B_TRANSLATE("Workspaces"));
235 workspaces
->SetAlignment(B_ALIGN_CENTER
);
237 fColumnsControl
= new BTextControl(B_TRANSLATE("Columns:"), "0",
238 new BMessage(kMsgWorkspaceColumnsChanged
));
239 fColumnsControl
->SetAlignment(B_ALIGN_RIGHT
, B_ALIGN_LEFT
);
240 fRowsControl
= new BTextControl(B_TRANSLATE("Rows:"), "0",
241 new BMessage(kMsgWorkspaceRowsChanged
));
242 fRowsControl
->SetAlignment(B_ALIGN_RIGHT
, B_ALIGN_LEFT
);
244 float tiny
= be_control_look
->DefaultItemSpacing() / 4;
245 screenBox
->AddChild(BLayoutBuilder::Group
<>()
246 .AddGroup(B_VERTICAL
, B_USE_SMALL_SPACING
)
250 .Add(fColumnsControl
->CreateLabelLayoutItem(), 0, 0)
251 .Add(BSpaceLayoutItem::CreateHorizontalStrut(
252 B_USE_SMALL_SPACING
), 1, 0)
253 .Add(fColumnsControl
->CreateTextViewLayoutItem(), 2, 0)
254 .Add(BSpaceLayoutItem::CreateHorizontalStrut(tiny
), 3, 0)
255 .Add(_CreateColumnRowButton(true, false), 4, 0)
256 .Add(_CreateColumnRowButton(true, true), 5, 0)
258 .Add(fRowsControl
->CreateLabelLayoutItem(), 0, 1)
259 .Add(BSpaceLayoutItem::CreateHorizontalStrut(
260 B_USE_SMALL_SPACING
), 1, 1)
261 .Add(fRowsControl
->CreateTextViewLayoutItem(), 2, 1)
262 .Add(BSpaceLayoutItem::CreateHorizontalStrut(tiny
), 3, 1)
263 .Add(_CreateColumnRowButton(false, false), 4, 1)
264 .Add(_CreateColumnRowButton(false, true), 5, 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 // TODO: we don't support getting the screen's preferred settings
510 /* fDefaultsButton = new BButton(buttonRect, "DefaultsButton", "Defaults",
511 new BMessage(BUTTON_DEFAULTS_MSG));*/
513 fApplyButton
= new BButton("ApplyButton", B_TRANSLATE("Apply"),
514 new BMessage(BUTTON_APPLY_MSG
));
515 fApplyButton
->SetEnabled(false);
516 BLayoutBuilder::Group
<>(outerControlsView
)
518 .AddGroup(B_HORIZONTAL
)
522 fRevertButton
= new BButton("RevertButton", B_TRANSLATE("Revert"),
523 new BMessage(BUTTON_REVERT_MSG
));
524 fRevertButton
->SetEnabled(false);
526 BLayoutBuilder::Group
<>(this, B_VERTICAL
, B_USE_DEFAULT_SPACING
)
527 .AddGroup(B_HORIZONTAL
)
528 .AddGroup(B_VERTICAL
, 0, 1)
529 .AddStrut(floorf(controlsBox
->TopBorderOffset()) - 1)
534 .AddGroup(B_HORIZONTAL
, B_USE_DEFAULT_SPACING
)
538 .SetInsets(B_USE_DEFAULT_SPACING
);
545 ScreenWindow::~ScreenWindow()
552 ScreenWindow::QuitRequested()
554 fSettings
->SetWindowFrame(Frame());
556 // Write mode of workspace 0 (the boot workspace) to the vesa settings file
557 screen_mode vesaMode
;
558 if (fBootWorkspaceApplied
&& fScreenMode
.Get(vesaMode
, 0) == B_OK
) {
559 status_t status
= _WriteVesaModeFile(vesaMode
);
561 BString warning
= B_TRANSLATE("Could not write VESA mode settings"
563 warning
<< strerror(status
);
564 BAlert
* alert
= new BAlert(B_TRANSLATE("Warning"),
565 warning
.String(), B_TRANSLATE("OK"), NULL
,
566 NULL
, B_WIDTH_AS_USUAL
, B_WARNING_ALERT
);
567 alert
->SetFlags(alert
->Flags() | B_CLOSE_ON_ESCAPE
);
572 be_app
->PostMessage(B_QUIT_REQUESTED
);
574 return BWindow::QuitRequested();
578 /*! Update resolution list according to combine mode
579 (some resolutions may not be combinable due to memory restrictions).
582 ScreenWindow::_CheckResolutionMenu()
584 for (int32 i
= 0; i
< fResolutionMenu
->CountItems(); i
++)
585 fResolutionMenu
->ItemAt(i
)->SetEnabled(false);
587 for (int32 i
= 0; i
< fScreenMode
.CountModes(); i
++) {
588 screen_mode mode
= fScreenMode
.ModeAt(i
);
589 if (mode
.combine
!= fSelected
.combine
)
593 name
<< mode
.width
<< " x " << mode
.height
;
595 BMenuItem
*item
= fResolutionMenu
->FindItem(name
.String());
597 item
->SetEnabled(true);
602 /*! Update color and refresh options according to current mode
603 (a color space is made active if there is any mode with
604 given resolution and this colour space; same applies for
605 refresh rate, though "Other…" is always possible)
608 ScreenWindow::_CheckColorMenu()
610 int32 supportsAnything
= false;
613 for (int32 i
= 0; i
< kColorSpaceCount
; i
++) {
614 if ((fSupportedColorSpaces
& (1 << i
)) == 0)
617 bool supported
= false;
619 for (int32 j
= 0; j
< fScreenMode
.CountModes(); j
++) {
620 screen_mode mode
= fScreenMode
.ModeAt(j
);
622 if (fSelected
.width
== mode
.width
623 && fSelected
.height
== mode
.height
624 && kColorSpaces
[i
].space
== mode
.space
625 && fSelected
.combine
== mode
.combine
) {
626 supportsAnything
= true;
632 BMenuItem
* item
= fColorsMenu
->ItemAt(index
++);
634 item
->SetEnabled(supported
);
637 fColorsField
->SetEnabled(supportsAnything
);
639 if (!supportsAnything
)
642 // Make sure a valid item is selected
644 BMenuItem
* item
= fColorsMenu
->FindMarked();
645 bool changed
= false;
647 if (item
!= fUserSelectedColorSpace
) {
648 if (fUserSelectedColorSpace
!= NULL
649 && fUserSelectedColorSpace
->IsEnabled()) {
650 fUserSelectedColorSpace
->SetMarked(true);
651 item
= fUserSelectedColorSpace
;
655 if (item
!= NULL
&& !item
->IsEnabled()) {
656 // find the next best item
657 int32 index
= fColorsMenu
->IndexOf(item
);
660 for (int32 i
= index
+ 1; i
< fColorsMenu
->CountItems(); i
++) {
661 item
= fColorsMenu
->ItemAt(i
);
662 if (item
->IsEnabled()) {
668 // search backwards as well
669 for (int32 i
= index
- 1; i
>= 0; i
--) {
670 item
= fColorsMenu
->ItemAt(i
);
671 if (item
->IsEnabled())
676 item
->SetMarked(true);
681 // Update selected space
683 BMessage
* message
= item
->Message();
685 if (message
->FindInt32("space", &space
) == B_OK
) {
686 fSelected
.space
= (color_space
)space
;
693 /*! Enable/disable refresh options according to current mode. */
695 ScreenWindow::_CheckRefreshMenu()
698 if (fScreenMode
.GetRefreshLimits(fSelected
, min
, max
) != B_OK
|| min
== max
)
701 for (int32 i
= fRefreshMenu
->CountItems(); i
-- > 0;) {
702 BMenuItem
* item
= fRefreshMenu
->ItemAt(i
);
703 BMessage
* message
= item
->Message();
705 if (message
!= NULL
&& message
->FindFloat("refresh", &refresh
) == B_OK
)
706 item
->SetEnabled(refresh
>= min
&& refresh
<= max
);
711 /*! Activate appropriate menu item according to selected refresh rate */
713 ScreenWindow::_UpdateRefreshControl()
715 for (int32 i
= 0; i
< fRefreshMenu
->CountItems(); i
++) {
716 BMenuItem
* item
= fRefreshMenu
->ItemAt(i
);
717 if (item
->Message()->FindFloat("refresh") == fSelected
.refresh
) {
718 item
->SetMarked(true);
719 // "Other" items only contains a refresh rate when active
720 fOtherRefresh
->SetLabel(B_TRANSLATE("Other" B_UTF8_ELLIPSIS
));
725 // this is a non-standard refresh rate
726 if (fOtherRefresh
!= NULL
) {
727 fOtherRefresh
->Message()->ReplaceFloat("refresh", fSelected
.refresh
);
728 fOtherRefresh
->SetMarked(true);
731 refresh_rate_to_string(fSelected
.refresh
, string
);
732 fRefreshMenu
->Superitem()->SetLabel(string
.String());
734 string
.Append(B_TRANSLATE("/other" B_UTF8_ELLIPSIS
));
735 fOtherRefresh
->SetLabel(string
.String());
741 ScreenWindow::_UpdateMonitorView()
743 BMessage
updateMessage(UPDATE_DESKTOP_MSG
);
744 updateMessage
.AddInt32("width", fSelected
.width
);
745 updateMessage
.AddInt32("height", fSelected
.height
);
747 PostMessage(&updateMessage
, fMonitorView
);
752 ScreenWindow::_UpdateControls()
754 _UpdateWorkspaceButtons();
756 BMenuItem
* item
= fSwapDisplaysMenu
->ItemAt((int32
)fSelected
.swap_displays
);
757 if (item
!= NULL
&& !item
->IsMarked())
758 item
->SetMarked(true);
760 item
= fUseLaptopPanelMenu
->ItemAt((int32
)fSelected
.use_laptop_panel
);
761 if (item
!= NULL
&& !item
->IsMarked())
762 item
->SetMarked(true);
764 for (int32 i
= 0; i
< fTVStandardMenu
->CountItems(); i
++) {
765 item
= fTVStandardMenu
->ItemAt(i
);
768 item
->Message()->FindInt32("tv_standard", (int32
*)&tvStandard
);
769 if (tvStandard
== fSelected
.tv_standard
) {
770 if (!item
->IsMarked())
771 item
->SetMarked(true);
776 _CheckResolutionMenu();
781 resolution_to_string(fSelected
, string
);
782 item
= fResolutionMenu
->FindItem(string
.String());
785 if (!item
->IsMarked())
786 item
->SetMarked(true);
788 // this is bad luck - if mode has been set via screen references,
789 // this case cannot occur; there are three possible solutions:
790 // 1. add a new resolution to list
791 // - we had to remove it as soon as a "valid" one is selected
792 // - we don't know which frequencies/bit depths are supported
793 // - as long as we haven't the GMT formula to create
794 // parameters for any resolution given, we cannot
795 // really set current mode - it's just not in the list
796 // 2. choose nearest resolution
797 // - probably a good idea, but implies coding and testing
798 // 3. choose lowest resolution
799 // - do you really think we are so lazy? yes, we are
800 item
= fResolutionMenu
->ItemAt(0);
802 item
->SetMarked(true);
804 // okay - at least we set menu label to active resolution
805 fResolutionMenu
->Superitem()->SetLabel(string
.String());
808 // mark active combine mode
809 for (int32 i
= 0; i
< kCombineModeCount
; i
++) {
810 if (kCombineModes
[i
].mode
== fSelected
.combine
) {
811 item
= fCombineMenu
->ItemAt(i
);
812 if (item
!= NULL
&& !item
->IsMarked())
813 item
->SetMarked(true);
818 item
= fColorsMenu
->ItemAt(0);
820 for (int32 i
= 0, index
= 0; i
< kColorSpaceCount
; i
++) {
821 if ((fSupportedColorSpaces
& (1 << i
)) == 0)
824 if (kColorSpaces
[i
].space
== fSelected
.space
) {
825 item
= fColorsMenu
->ItemAt(index
);
832 if (item
!= NULL
&& !item
->IsMarked())
833 item
->SetMarked(true);
836 _UpdateMonitorView();
837 _UpdateRefreshControl();
839 _CheckApplyEnabled();
843 /*! Reflect active mode in chosen settings */
845 ScreenWindow::_UpdateActiveMode()
847 _UpdateActiveMode(current_workspace());
852 ScreenWindow::_UpdateActiveMode(int32 workspace
)
854 // Usually, this function gets called after a mode
855 // has been set manually; still, as the graphics driver
856 // is free to fiddle with mode passed, we better ask
857 // what kind of mode we actually got
858 if (fScreenMode
.Get(fActive
, workspace
) == B_OK
) {
862 _BuildSupportedColorSpaces();
869 ScreenWindow::_UpdateWorkspaceButtons()
873 BPrivate::get_workspaces_layout(&columns
, &rows
);
876 snprintf(text
, sizeof(text
), "%" B_PRId32
, columns
);
877 fColumnsControl
->SetText(text
);
879 snprintf(text
, sizeof(text
), "%" B_PRId32
, rows
);
880 fRowsControl
->SetText(text
);
882 _GetColumnRowButton(true, false)->SetEnabled(columns
!= 1 && rows
!= 32);
883 _GetColumnRowButton(true, true)->SetEnabled((columns
+ 1) * rows
< 32);
884 _GetColumnRowButton(false, false)->SetEnabled(rows
!= 1 && columns
!= 32);
885 _GetColumnRowButton(false, true)->SetEnabled(columns
* (rows
+ 1) < 32);
890 ScreenWindow::ScreenChanged(BRect frame
, color_space mode
)
892 // move window on screen, if necessary
893 if (frame
.right
<= Frame().right
894 && frame
.bottom
<= Frame().bottom
) {
895 MoveTo((frame
.Width() - Frame().Width()) / 2,
896 (frame
.Height() - Frame().Height()) / 2);
902 ScreenWindow::WorkspaceActivated(int32 workspace
, bool state
)
904 if (fScreenMode
.GetOriginalMode(fOriginal
, workspace
) == B_OK
) {
905 _UpdateActiveMode(workspace
);
907 BMessage
message(UPDATE_DESKTOP_COLOR_MSG
);
908 PostMessage(&message
, fMonitorView
);
914 ScreenWindow::MessageReceived(BMessage
* message
)
916 switch (message
->what
) {
917 case WORKSPACE_CHECK_MSG
:
918 _CheckApplyEnabled();
921 case kMsgWorkspaceLayoutChanged
:
925 message
->FindInt32("delta_x", &deltaX
);
926 message
->FindInt32("delta_y", &deltaY
);
928 if (deltaX
== 0 && deltaY
== 0)
933 BPrivate::get_workspaces_layout(&newColumns
, &newRows
);
935 newColumns
+= deltaX
;
937 BPrivate::set_workspaces_layout(newColumns
, newRows
);
939 _UpdateWorkspaceButtons();
940 _CheckApplyEnabled();
944 case kMsgWorkspaceColumnsChanged
:
946 uint32 newColumns
= strtoul(fColumnsControl
->Text(), NULL
, 10);
949 BPrivate::get_workspaces_layout(NULL
, &rows
);
950 BPrivate::set_workspaces_layout(newColumns
, rows
);
952 _UpdateWorkspaceButtons();
953 _CheckApplyEnabled();
957 case kMsgWorkspaceRowsChanged
:
959 uint32 newRows
= strtoul(fRowsControl
->Text(), NULL
, 10);
962 BPrivate::get_workspaces_layout(&columns
, NULL
);
963 BPrivate::set_workspaces_layout(columns
, newRows
);
965 _UpdateWorkspaceButtons();
966 _CheckApplyEnabled();
970 case POP_RESOLUTION_MSG
:
972 message
->FindInt32("width", &fSelected
.width
);
973 message
->FindInt32("height", &fSelected
.height
);
978 _UpdateMonitorView();
979 _UpdateRefreshControl();
981 _CheckApplyEnabled();
988 if (message
->FindInt32("space", &space
) != B_OK
)
992 if (message
->FindInt32("index", &index
) == B_OK
993 && fColorsMenu
->ItemAt(index
) != NULL
)
994 fUserSelectedColorSpace
= fColorsMenu
->ItemAt(index
);
996 fSelected
.space
= (color_space
)space
;
999 _CheckApplyEnabled();
1003 case POP_REFRESH_MSG
:
1005 message
->FindFloat("refresh", &fSelected
.refresh
);
1006 fOtherRefresh
->SetLabel(B_TRANSLATE("Other" B_UTF8_ELLIPSIS
));
1007 // revert "Other…" label - it might have a refresh rate prefix
1009 _CheckApplyEnabled();
1013 case POP_OTHER_REFRESH_MSG
:
1015 // make sure menu shows something useful
1016 _UpdateRefreshControl();
1018 float min
= 0, max
= 999;
1019 fScreenMode
.GetRefreshLimits(fSelected
, min
, max
);
1020 if (min
< gMinRefresh
)
1022 if (max
> gMaxRefresh
)
1026 if (fScreenMode
.GetMonitorInfo(info
) == B_OK
) {
1027 min
= max_c(info
.min_vertical_frequency
, min
);
1028 max
= min_c(info
.max_vertical_frequency
, max
);
1031 RefreshWindow
*fRefreshWindow
= new RefreshWindow(
1032 fRefreshField
->ConvertToScreen(B_ORIGIN
), fSelected
.refresh
,
1034 fRefreshWindow
->Show();
1038 case SET_CUSTOM_REFRESH_MSG
:
1040 // user pressed "done" in "Other…" refresh dialog;
1041 // select the refresh rate chosen
1042 message
->FindFloat("refresh", &fSelected
.refresh
);
1044 _UpdateRefreshControl();
1045 _CheckApplyEnabled();
1049 case POP_COMBINE_DISPLAYS_MSG
:
1051 // new combine mode has bee chosen
1053 if (message
->FindInt32("mode", &mode
) == B_OK
)
1054 fSelected
.combine
= (combine_mode
)mode
;
1056 _CheckResolutionMenu();
1057 _CheckApplyEnabled();
1061 case POP_SWAP_DISPLAYS_MSG
:
1062 message
->FindBool("swap", &fSelected
.swap_displays
);
1063 _CheckApplyEnabled();
1066 case POP_USE_LAPTOP_PANEL_MSG
:
1067 message
->FindBool("use", &fSelected
.use_laptop_panel
);
1068 _CheckApplyEnabled();
1071 case POP_TV_STANDARD_MSG
:
1072 message
->FindInt32("tv_standard", (int32
*)&fSelected
.tv_standard
);
1073 _CheckApplyEnabled();
1076 case BUTTON_LAUNCH_BACKGROUNDS_MSG
:
1077 if (be_roster
->Launch(kBackgroundsSignature
) == B_ALREADY_RUNNING
) {
1079 be_roster
->GetAppInfo(kBackgroundsSignature
, &info
);
1080 be_roster
->ActivateApp(info
.team
);
1084 case BUTTON_DEFAULTS_MSG
:
1086 // TODO: get preferred settings of screen
1087 fSelected
.width
= 640;
1088 fSelected
.height
= 480;
1089 fSelected
.space
= B_CMAP8
;
1090 fSelected
.refresh
= 60.0;
1091 fSelected
.combine
= kCombineDisable
;
1092 fSelected
.swap_displays
= false;
1093 fSelected
.use_laptop_panel
= false;
1094 fSelected
.tv_standard
= 0;
1096 // TODO: workspace defaults
1102 case BUTTON_UNDO_MSG
:
1103 fUndoScreenMode
.Revert();
1104 _UpdateActiveMode();
1107 case BUTTON_REVERT_MSG
:
1110 fBootWorkspaceApplied
= false;
1112 // ScreenMode::Revert() assumes that we first set the correct
1113 // number of workspaces
1115 BPrivate::set_workspaces_layout(fOriginalWorkspacesColumns
,
1116 fOriginalWorkspacesRows
);
1117 _UpdateWorkspaceButtons();
1119 fScreenMode
.Revert();
1120 _UpdateActiveMode();
1124 case BUTTON_APPLY_MSG
:
1128 case MAKE_INITIAL_MSG
:
1129 // user pressed "keep" in confirmation dialog
1131 _UpdateActiveMode();
1135 BWindow::MessageReceived(message
);
1142 ScreenWindow::_WriteVesaModeFile(const screen_mode
& mode
) const
1145 status_t status
= find_directory(B_USER_SETTINGS_DIRECTORY
, &path
, true);
1149 path
.Append("kernel/drivers");
1150 status
= create_directory(path
.Path(), 0755);
1154 path
.Append("vesa");
1156 status
= file
.SetTo(path
.Path(), B_CREATE_FILE
| B_WRITE_ONLY
| B_ERASE_FILE
);
1161 snprintf(buffer
, sizeof(buffer
), "mode %" B_PRId32
" %" B_PRId32
" %"
1162 B_PRId32
"\n", mode
.width
, mode
.height
, mode
.BitsPerPixel());
1164 ssize_t bytesWritten
= file
.Write(buffer
, strlen(buffer
));
1165 if (bytesWritten
< B_OK
)
1166 return bytesWritten
;
1173 ScreenWindow::_CreateColumnRowButton(bool columns
, bool plus
)
1175 BMessage
* message
= new BMessage(kMsgWorkspaceLayoutChanged
);
1176 message
->AddInt32("delta_x", columns
? (plus
? 1 : -1) : 0);
1177 message
->AddInt32("delta_y", !columns
? (plus
? 1 : -1) : 0);
1179 BButton
* button
= new BButton(plus
? "+" : "\xe2\x88\x92", message
);
1180 button
->SetFontSize(be_plain_font
->Size() * 0.9);
1182 BSize size
= button
->MinSize();
1183 size
.width
= button
->StringWidth("+") + 16;
1184 button
->SetExplicitMinSize(size
);
1185 button
->SetExplicitMaxSize(size
);
1187 fWorkspacesButtons
[(columns
? 0 : 2) + (plus
? 1 : 0)] = button
;
1193 ScreenWindow::_GetColumnRowButton(bool columns
, bool plus
)
1195 return fWorkspacesButtons
[(columns
? 0 : 2) + (plus
? 1 : 0)];
1200 ScreenWindow::_BuildSupportedColorSpaces()
1202 fSupportedColorSpaces
= 0;
1204 for (int32 i
= 0; i
< kColorSpaceCount
; i
++) {
1205 for (int32 j
= 0; j
< fScreenMode
.CountModes(); j
++) {
1206 if (fScreenMode
.ModeAt(j
).space
== kColorSpaces
[i
].space
) {
1207 fSupportedColorSpaces
|= 1 << i
;
1216 ScreenWindow::_CheckApplyEnabled()
1218 bool applyEnabled
= true;
1220 if (fSelected
== fActive
) {
1221 applyEnabled
= false;
1222 if (fAllWorkspacesItem
->IsMarked()) {
1223 screen_mode screenMode
;
1224 const int32 workspaceCount
= count_workspaces();
1225 for (int32 i
= 0; i
< workspaceCount
; i
++) {
1226 fScreenMode
.Get(screenMode
, i
);
1227 if (screenMode
!= fSelected
) {
1228 applyEnabled
= true;
1235 fApplyButton
->SetEnabled(applyEnabled
);
1239 BPrivate::get_workspaces_layout(&columns
, &rows
);
1241 fRevertButton
->SetEnabled(columns
!= fOriginalWorkspacesColumns
1242 || rows
!= fOriginalWorkspacesRows
1243 || fSelected
!= fOriginal
);
1248 ScreenWindow::_UpdateOriginal()
1250 BPrivate::get_workspaces_layout(&fOriginalWorkspacesColumns
,
1251 &fOriginalWorkspacesRows
);
1253 fScreenMode
.Get(fOriginal
);
1254 fScreenMode
.UpdateOriginalModes();
1259 ScreenWindow::_UpdateMonitor()
1262 float diagonalInches
;
1263 status_t status
= fScreenMode
.GetMonitorInfo(info
, &diagonalInches
);
1264 if (status
== B_OK
) {
1266 snprintf(text
, sizeof(text
), "%s%s%s %g\"", info
.vendor
,
1267 info
.name
[0] ? " " : "", info
.name
, diagonalInches
);
1269 fMonitorInfo
->SetText(text
);
1271 if (fMonitorInfo
->IsHidden())
1272 fMonitorInfo
->Show();
1274 if (!fMonitorInfo
->IsHidden())
1275 fMonitorInfo
->Hide();
1278 // Add info about the graphics device
1280 accelerant_device_info deviceInfo
;
1282 if (fScreenMode
.GetDeviceInfo(deviceInfo
) == B_OK
) {
1283 BString deviceString
;
1285 if (deviceInfo
.name
[0] && deviceInfo
.chipset
[0]) {
1286 deviceString
.SetToFormat("%s (%s)", deviceInfo
.name
,
1287 deviceInfo
.chipset
);
1288 } else if (deviceInfo
.name
[0] || deviceInfo
.chipset
[0]) {
1290 = deviceInfo
.name
[0] ? deviceInfo
.name
: deviceInfo
.chipset
;
1293 fDeviceInfo
->SetText(deviceString
);
1301 if (status
== B_OK
) {
1302 if (info
.min_horizontal_frequency
!= 0
1303 && info
.min_vertical_frequency
!= 0
1304 && info
.max_pixel_clock
!= 0) {
1305 length
= snprintf(text
, sizeof(text
),
1306 B_TRANSLATE("Horizonal frequency:\t%lu - %lu kHz\n"
1307 "Vertical frequency:\t%lu - %lu Hz\n\n"
1308 "Maximum pixel clock:\t%g MHz"),
1309 info
.min_horizontal_frequency
, info
.max_horizontal_frequency
,
1310 info
.min_vertical_frequency
, info
.max_vertical_frequency
,
1311 info
.max_pixel_clock
/ 1000.0);
1313 if (info
.serial_number
[0] && length
< sizeof(text
)) {
1314 length
+= snprintf(text
+ length
, sizeof(text
) - length
,
1315 B_TRANSLATE("%sSerial no.: %s"), length
? "\n\n" : "",
1316 info
.serial_number
);
1317 if (info
.produced
.week
!= 0 && info
.produced
.year
!= 0
1318 && length
< sizeof(text
)) {
1319 length
+= snprintf(text
+ length
, sizeof(text
) - length
,
1320 " (%u/%u)", info
.produced
.week
, info
.produced
.year
);
1326 fMonitorView
->SetToolTip(text
);
1331 ScreenWindow::_UpdateColorLabel()
1334 string
<< fSelected
.BitsPerPixel() << " " << B_TRANSLATE("bits/pixel");
1335 fColorsMenu
->Superitem()->SetLabel(string
.String());
1340 ScreenWindow::_Apply()
1342 // make checkpoint, so we can undo these changes
1343 fUndoScreenMode
.UpdateOriginalModes();
1345 status_t status
= fScreenMode
.Set(fSelected
);
1346 if (status
== B_OK
) {
1347 // use the mode that has eventually been set and
1348 // thus we know to be working; it can differ from
1349 // the mode selected by user due to hardware limitation
1350 display_mode newMode
;
1351 BScreen
screen(this);
1352 screen
.GetMode(&newMode
);
1354 if (fAllWorkspacesItem
->IsMarked()) {
1355 int32 originatingWorkspace
= current_workspace();
1356 const int32 workspaceCount
= count_workspaces();
1357 for (int32 i
= 0; i
< workspaceCount
; i
++) {
1358 if (i
!= originatingWorkspace
)
1359 screen
.SetMode(i
, &newMode
, true);
1361 fBootWorkspaceApplied
= true;
1363 if (current_workspace() == 0)
1364 fBootWorkspaceApplied
= true;
1367 fActive
= fSelected
;
1369 // TODO: only show alert when this is an unknown mode
1370 BWindow
* window
= new AlertWindow(this);
1374 snprintf(message
, sizeof(message
),
1375 B_TRANSLATE("The screen mode could not be set:\n\t%s\n"),
1376 screen_errors(status
));
1377 BAlert
* alert
= new BAlert(B_TRANSLATE("Warning"), message
,
1378 B_TRANSLATE("OK"), NULL
, NULL
,
1379 B_WIDTH_AS_USUAL
, B_WARNING_ALERT
);
1380 alert
->SetFlags(alert
->Flags() | B_CLOSE_ON_ESCAPE
);