tcp: Fix 64 bit build with debugging features enabled.
[haiku.git] / src / preferences / screen / ScreenWindow.cpp
blobc3bfb28696696fed975a0fd513bb02d0d4ab239a
1 /*
2 * Copyright 2001-2013, Haiku, Inc.
3 * Distributed under the terms of the MIT License.
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
18 #include "ScreenWindow.h"
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <strings.h>
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 <String.h>
44 #include <StringView.h>
45 #include <Roster.h>
46 #include <Window.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"
55 #include "Utility.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
59 * adopted.
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
62 * be using this.
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
76 static const struct {
77 color_space space;
78 int32 bits_per_pixel;
79 const char* label;
80 } kColorSpaces[] = {
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
96 static const struct {
97 combine_mode mode;
98 const char *name;
99 } kCombineModes[] = {
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]);
108 static BString
109 tv_standard_to_string(uint32 mode)
111 switch (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";
122 default:
124 BString name;
125 name << "??? (" << mode << ")";
127 return name;
133 static void
134 resolution_to_string(screen_mode& mode, BString &string)
136 string << mode.width << " x " << mode.height;
140 static void
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,
145 refresh);
146 string.UnlockBuffer();
148 if (appendUnit)
149 string << " " << B_TRANSLATE("Hz");
153 static const char*
154 screen_errors(status_t status)
156 switch (status) {
157 case B_ENTRY_NOT_FOUND:
158 return B_TRANSLATE("Unknown mode");
159 // TODO: add more?
161 default:
162 return strerror(status);
167 // #pragma mark -
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),
175 fIsVesa(false),
176 fBootWorkspaceApplied(false),
177 fOtherRefresh(NULL),
178 fScreenMode(this),
179 fUndoScreenMode(this),
180 fModified(false)
182 BScreen screen(this);
184 accelerant_device_info info;
185 if (screen.GetDeviceInfo(&info) == B_OK
186 && !strcasecmp(info.chipset, "VESA"))
187 fIsVesa = true;
189 _UpdateOriginal();
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"),
198 true, true);
199 fAllWorkspacesItem = new BMenuItem(B_TRANSLATE("All workspaces"),
200 new BMessage(WORKSPACE_CHECK_MSG));
201 popUpMenu->AddItem(fAllWorkspacesItem);
202 BMenuItem *item = new BMenuItem(B_TRANSLATE("Current workspace"),
203 new BMessage(WORKSPACE_CHECK_MSG));
205 popUpMenu->AddItem(item);
206 fAllWorkspacesItem->SetMarked(true);
208 BMenuField* workspaceMenuField = new BMenuField("WorkspaceMenu", NULL,
209 popUpMenu);
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)
247 .Add(workspaces)
248 .AddGrid(0.0, tiny)
249 // columns
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)
257 // rows
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)
265 .End()
266 .End()
267 .View());
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);
286 uint16 maxWidth = 0;
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)
294 continue;
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);
307 BString name;
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)
323 continue;
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:"),
337 fColorsMenu);
338 fColorsField->SetAlignment(B_ALIGN_RIGHT);
340 fRefreshMenu = new BPopUpMenu("refresh rate", true, true);
342 float min, max;
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
346 // later.
347 min = kRefreshRates[0];
348 max = kRefreshRates[kRefreshRateCount - 1];
351 if (min == max) {
352 // This is a special case for drivers that only support a single
353 // frequency, like the VESA driver
354 BString name;
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);
361 } else {
362 monitor_info info;
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)
370 continue;
372 BString name;
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:"),
387 fRefreshMenu);
388 fRefreshField->SetAlignment(B_ALIGN_RIGHT);
390 if (_IsVesa())
391 fRefreshField->Hide();
393 // enlarged area for multi-monitor settings
395 bool dummy;
396 uint32 dummy32;
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",
409 true, true);
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,
416 message));
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",
427 true, true);
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",
446 true, true);
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"),
452 message));
454 message = new BMessage(POP_USE_LAPTOP_PANEL_MSG);
455 message->AddBool("use", true);
456 fUseLaptopPanelMenu->AddItem(new BMenuItem(B_TRANSLATE("always"),
457 message));
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);
468 // arbitrary limit
469 uint32 i;
470 for (i = 0; i < 100; ++i) {
471 uint32 mode;
472 if (GetNthSupportedTVStandard(&screen, i, &mode) != B_OK)
473 break;
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)
507 .End();
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)
517 .AddGlue()
518 .AddGroup(B_HORIZONTAL)
519 .AddGlue()
520 .Add(fApplyButton);
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)
530 .Add(screenBox)
531 .End()
532 .Add(controlsBox, 2)
533 .End()
534 .AddGroup(B_HORIZONTAL, B_USE_DEFAULT_SPACING)
535 .Add(fRevertButton)
536 .AddGlue()
537 .End()
538 .SetInsets(B_USE_DEFAULT_SPACING);
540 _UpdateControls();
541 _UpdateMonitor();
545 ScreenWindow::~ScreenWindow()
547 delete fSettings;
551 bool
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);
560 if (status < B_OK) {
561 BString warning = B_TRANSLATE("Could not write VESA mode settings"
562 " file:\n\t");
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);
568 alert->Go();
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).
581 void
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)
590 continue;
592 BString name;
593 name << mode.width << " x " << mode.height;
595 BMenuItem *item = fResolutionMenu->FindItem(name.String());
596 if (item != NULL)
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)
607 void
608 ScreenWindow::_CheckColorMenu()
610 int32 supportsAnything = false;
611 int32 index = 0;
613 for (int32 i = 0; i < kColorSpaceCount; i++) {
614 if ((fSupportedColorSpaces & (1 << i)) == 0)
615 continue;
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;
627 supported = true;
628 break;
632 BMenuItem* item = fColorsMenu->ItemAt(index++);
633 if (item)
634 item->SetEnabled(supported);
637 fColorsField->SetEnabled(supportsAnything);
639 if (!supportsAnything)
640 return;
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;
652 changed = true;
655 if (item != NULL && !item->IsEnabled()) {
656 // find the next best item
657 int32 index = fColorsMenu->IndexOf(item);
658 bool found = false;
660 for (int32 i = index + 1; i < fColorsMenu->CountItems(); i++) {
661 item = fColorsMenu->ItemAt(i);
662 if (item->IsEnabled()) {
663 found = true;
664 break;
667 if (!found) {
668 // search backwards as well
669 for (int32 i = index - 1; i >= 0; i--) {
670 item = fColorsMenu->ItemAt(i);
671 if (item->IsEnabled())
672 break;
676 item->SetMarked(true);
677 changed = true;
680 if (changed) {
681 // Update selected space
683 BMessage* message = item->Message();
684 int32 space;
685 if (message->FindInt32("space", &space) == B_OK) {
686 fSelected.space = (color_space)space;
687 _UpdateColorLabel();
693 /*! Enable/disable refresh options according to current mode. */
694 void
695 ScreenWindow::_CheckRefreshMenu()
697 float min, max;
698 if (fScreenMode.GetRefreshLimits(fSelected, min, max) != B_OK || min == max)
699 return;
701 for (int32 i = fRefreshMenu->CountItems(); i-- > 0;) {
702 BMenuItem* item = fRefreshMenu->ItemAt(i);
703 BMessage* message = item->Message();
704 float refresh;
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 */
712 void
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));
721 return;
725 // this is a non-standard refresh rate
726 if (fOtherRefresh != NULL) {
727 fOtherRefresh->Message()->ReplaceFloat("refresh", fSelected.refresh);
728 fOtherRefresh->SetMarked(true);
730 BString string;
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());
740 void
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);
751 void
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);
767 uint32 tvStandard;
768 item->Message()->FindInt32("tv_standard", (int32 *)&tvStandard);
769 if (tvStandard == fSelected.tv_standard) {
770 if (!item->IsMarked())
771 item->SetMarked(true);
772 break;
776 _CheckResolutionMenu();
777 _CheckColorMenu();
778 _CheckRefreshMenu();
780 BString string;
781 resolution_to_string(fSelected, string);
782 item = fResolutionMenu->FindItem(string.String());
784 if (item != NULL) {
785 if (!item->IsMarked())
786 item->SetMarked(true);
787 } else {
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);
801 if (item)
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);
814 break;
818 item = fColorsMenu->ItemAt(0);
820 for (int32 i = 0, index = 0; i < kColorSpaceCount; i++) {
821 if ((fSupportedColorSpaces & (1 << i)) == 0)
822 continue;
824 if (kColorSpaces[i].space == fSelected.space) {
825 item = fColorsMenu->ItemAt(index);
826 break;
829 index++;
832 if (item != NULL && !item->IsMarked())
833 item->SetMarked(true);
835 _UpdateColorLabel();
836 _UpdateMonitorView();
837 _UpdateRefreshControl();
839 _CheckApplyEnabled();
843 /*! Reflect active mode in chosen settings */
844 void
845 ScreenWindow::_UpdateActiveMode()
847 _UpdateActiveMode(current_workspace());
851 void
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) {
859 fSelected = fActive;
861 _UpdateMonitor();
862 _BuildSupportedColorSpaces();
863 _UpdateControls();
868 void
869 ScreenWindow::_UpdateWorkspaceButtons()
871 uint32 columns;
872 uint32 rows;
873 BPrivate::get_workspaces_layout(&columns, &rows);
875 char text[32];
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);
889 void
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);
901 void
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);
913 void
914 ScreenWindow::MessageReceived(BMessage* message)
916 switch (message->what) {
917 case WORKSPACE_CHECK_MSG:
918 _CheckApplyEnabled();
919 break;
921 case kMsgWorkspaceLayoutChanged:
923 int32 deltaX = 0;
924 int32 deltaY = 0;
925 message->FindInt32("delta_x", &deltaX);
926 message->FindInt32("delta_y", &deltaY);
928 if (deltaX == 0 && deltaY == 0)
929 break;
931 uint32 newColumns;
932 uint32 newRows;
933 BPrivate::get_workspaces_layout(&newColumns, &newRows);
935 newColumns += deltaX;
936 newRows += deltaY;
937 BPrivate::set_workspaces_layout(newColumns, newRows);
939 _UpdateWorkspaceButtons();
940 _CheckApplyEnabled();
941 break;
944 case kMsgWorkspaceColumnsChanged:
946 uint32 newColumns = strtoul(fColumnsControl->Text(), NULL, 10);
948 uint32 rows;
949 BPrivate::get_workspaces_layout(NULL, &rows);
950 BPrivate::set_workspaces_layout(newColumns, rows);
952 _UpdateWorkspaceButtons();
953 _CheckApplyEnabled();
954 break;
957 case kMsgWorkspaceRowsChanged:
959 uint32 newRows = strtoul(fRowsControl->Text(), NULL, 10);
961 uint32 columns;
962 BPrivate::get_workspaces_layout(&columns, NULL);
963 BPrivate::set_workspaces_layout(columns, newRows);
965 _UpdateWorkspaceButtons();
966 _CheckApplyEnabled();
967 break;
970 case POP_RESOLUTION_MSG:
972 message->FindInt32("width", &fSelected.width);
973 message->FindInt32("height", &fSelected.height);
975 _CheckColorMenu();
976 _CheckRefreshMenu();
978 _UpdateMonitorView();
979 _UpdateRefreshControl();
981 _CheckApplyEnabled();
982 break;
985 case POP_COLORS_MSG:
987 int32 space;
988 if (message->FindInt32("space", &space) != B_OK)
989 break;
991 int32 index;
992 if (message->FindInt32("index", &index) == B_OK
993 && fColorsMenu->ItemAt(index) != NULL)
994 fUserSelectedColorSpace = fColorsMenu->ItemAt(index);
996 fSelected.space = (color_space)space;
997 _UpdateColorLabel();
999 _CheckApplyEnabled();
1000 break;
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();
1010 break;
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)
1021 min = gMinRefresh;
1022 if (max > gMaxRefresh)
1023 max = gMaxRefresh;
1025 monitor_info info;
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,
1033 min, max);
1034 fRefreshWindow->Show();
1035 break;
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();
1046 break;
1049 case POP_COMBINE_DISPLAYS_MSG:
1051 // new combine mode has bee chosen
1052 int32 mode;
1053 if (message->FindInt32("mode", &mode) == B_OK)
1054 fSelected.combine = (combine_mode)mode;
1056 _CheckResolutionMenu();
1057 _CheckApplyEnabled();
1058 break;
1061 case POP_SWAP_DISPLAYS_MSG:
1062 message->FindBool("swap", &fSelected.swap_displays);
1063 _CheckApplyEnabled();
1064 break;
1066 case POP_USE_LAPTOP_PANEL_MSG:
1067 message->FindBool("use", &fSelected.use_laptop_panel);
1068 _CheckApplyEnabled();
1069 break;
1071 case POP_TV_STANDARD_MSG:
1072 message->FindInt32("tv_standard", (int32 *)&fSelected.tv_standard);
1073 _CheckApplyEnabled();
1074 break;
1076 case BUTTON_LAUNCH_BACKGROUNDS_MSG:
1077 if (be_roster->Launch(kBackgroundsSignature) == B_ALREADY_RUNNING) {
1078 app_info info;
1079 be_roster->GetAppInfo(kBackgroundsSignature, &info);
1080 be_roster->ActivateApp(info.team);
1082 break;
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
1098 _UpdateControls();
1099 break;
1102 case BUTTON_UNDO_MSG:
1103 fUndoScreenMode.Revert();
1104 _UpdateActiveMode();
1105 break;
1107 case BUTTON_REVERT_MSG:
1109 fModified = false;
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();
1121 break;
1124 case BUTTON_APPLY_MSG:
1125 _Apply();
1126 break;
1128 case MAKE_INITIAL_MSG:
1129 // user pressed "keep" in confirmation dialog
1130 fModified = true;
1131 _UpdateActiveMode();
1132 break;
1134 default:
1135 BWindow::MessageReceived(message);
1136 break;
1141 status_t
1142 ScreenWindow::_WriteVesaModeFile(const screen_mode& mode) const
1144 BPath path;
1145 status_t status = find_directory(B_USER_SETTINGS_DIRECTORY, &path, true);
1146 if (status < B_OK)
1147 return status;
1149 path.Append("kernel/drivers");
1150 status = create_directory(path.Path(), 0755);
1151 if (status < B_OK)
1152 return status;
1154 path.Append("vesa");
1155 BFile file;
1156 status = file.SetTo(path.Path(), B_CREATE_FILE | B_WRITE_ONLY | B_ERASE_FILE);
1157 if (status < B_OK)
1158 return status;
1160 char buffer[256];
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;
1168 return B_OK;
1172 BButton*
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;
1188 return button;
1192 BButton*
1193 ScreenWindow::_GetColumnRowButton(bool columns, bool plus)
1195 return fWorkspacesButtons[(columns ? 0 : 2) + (plus ? 1 : 0)];
1199 void
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;
1208 break;
1215 void
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;
1229 break;
1235 fApplyButton->SetEnabled(applyEnabled);
1237 uint32 columns;
1238 uint32 rows;
1239 BPrivate::get_workspaces_layout(&columns, &rows);
1241 fRevertButton->SetEnabled(columns != fOriginalWorkspacesColumns
1242 || rows != fOriginalWorkspacesRows
1243 || fSelected != fOriginal);
1247 void
1248 ScreenWindow::_UpdateOriginal()
1250 BPrivate::get_workspaces_layout(&fOriginalWorkspacesColumns,
1251 &fOriginalWorkspacesRows);
1253 fScreenMode.Get(fOriginal);
1254 fScreenMode.UpdateOriginalModes();
1258 void
1259 ScreenWindow::_UpdateMonitor()
1261 monitor_info info;
1262 float diagonalInches;
1263 status_t status = fScreenMode.GetMonitorInfo(info, &diagonalInches);
1264 if (status == B_OK) {
1265 char text[512];
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();
1273 } else {
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]) {
1289 deviceString
1290 = deviceInfo.name[0] ? deviceInfo.name : deviceInfo.chipset;
1293 fDeviceInfo->SetText(deviceString);
1297 char text[512];
1298 size_t length = 0;
1299 text[0] = 0;
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);
1325 if (text[0])
1326 fMonitorView->SetToolTip(text);
1330 void
1331 ScreenWindow::_UpdateColorLabel()
1333 BString string;
1334 string << fSelected.BitsPerPixel() << " " << B_TRANSLATE("bits/pixel");
1335 fColorsMenu->Superitem()->SetLabel(string.String());
1339 void
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;
1362 } else {
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);
1371 window->Show();
1372 } else {
1373 char message[256];
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);
1381 alert->Go();