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