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