xref: /haiku/src/preferences/screen/ScreenWindow.cpp (revision 7457ccb4b2f4786525d3b7bda42598487d57ab7d)
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 	BBox* screenBox = new BBox("screen box");
216 	BGroupLayout* layout = new BGroupLayout(B_VERTICAL, B_USE_SMALL_SPACING);
217 	layout->SetInsets(B_USE_DEFAULT_SPACING, B_USE_DEFAULT_SPACING,
218 		B_USE_DEFAULT_SPACING, B_USE_DEFAULT_SPACING);
219 	screenBox->SetLayout(layout);
220 
221 	fMonitorInfo = new BStringView("monitor info", "");
222 	fMonitorInfo->SetAlignment(B_ALIGN_CENTER);
223 	screenBox->AddChild(fMonitorInfo);
224 
225 	fDeviceInfo = new BStringView("monitor info", "");
226 	fDeviceInfo->SetAlignment(B_ALIGN_CENTER);
227 	screenBox->AddChild(fDeviceInfo);
228 
229 	float scaling = std::max(1.0f, be_plain_font->Size() / 12.0f);
230 	fMonitorView = new MonitorView(BRect(0.0, 0.0, 80.0 * scaling,
231 			80.0 * scaling), "monitor", screen.Frame().IntegerWidth() + 1,
232 		screen.Frame().IntegerHeight() + 1);
233 	screenBox->AddChild(fMonitorView);
234 
235 	BStringView* workspaces = new BStringView("workspaces",
236 		B_TRANSLATE("Workspaces"));
237 	workspaces->SetAlignment(B_ALIGN_CENTER);
238 
239 	fColumnsControl = new BSpinner("columns", B_TRANSLATE("Columns:"),
240 		new BMessage(kMsgWorkspaceColumnsChanged));
241 	fColumnsControl->SetAlignment(B_ALIGN_RIGHT);
242 	fColumnsControl->SetRange(1, 32);
243 
244 	fRowsControl = new BSpinner("rows", B_TRANSLATE("Rows:"),
245 		new BMessage(kMsgWorkspaceRowsChanged));
246 	fRowsControl->SetAlignment(B_ALIGN_RIGHT);
247 	fRowsControl->SetRange(1, 32);
248 
249 	uint32 columns;
250 	uint32 rows;
251 	BPrivate::get_workspaces_layout(&columns, &rows);
252 	fColumnsControl->SetValue(columns);
253 	fRowsControl->SetValue(rows);
254 
255 	screenBox->AddChild(BLayoutBuilder::Group<>()
256 		.AddGroup(B_VERTICAL, B_USE_SMALL_SPACING)
257 			.Add(workspaces)
258 			.AddGrid(B_USE_DEFAULT_SPACING, B_USE_SMALL_SPACING)
259 				// columns
260 				.Add(fColumnsControl->CreateLabelLayoutItem(), 0, 0)
261 				.Add(fColumnsControl->CreateTextViewLayoutItem(), 1, 0)
262 				// rows
263 				.Add(fRowsControl->CreateLabelLayoutItem(), 0, 1)
264 				.Add(fRowsControl->CreateTextViewLayoutItem(), 1, 1)
265 				.End()
266 			.End()
267 		.View());
268 
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);
274 
275 	// box on the right with screen resolution, etc.
276 
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);
283 
284 	fResolutionMenu = new BPopUpMenu("resolution", true, true);
285 
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);
292 
293 		if (mode.width == previousWidth && mode.height == previousHeight)
294 			continue;
295 
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;
302 
303 		BMessage* message = new BMessage(POP_RESOLUTION_MSG);
304 		message->AddInt32("width", mode.width);
305 		message->AddInt32("height", mode.height);
306 
307 		BString name;
308 		name << mode.width << " x " << mode.height;
309 
310 		fResolutionMenu->AddItem(new BMenuItem(name.String(), message));
311 	}
312 
313 	fMonitorView->SetMaxResolution(maxWidth, maxHeight);
314 
315 	fResolutionField = new BMenuField("ResolutionMenu",
316 		B_TRANSLATE("Resolution:"), fResolutionMenu);
317 	fResolutionField->SetAlignment(B_ALIGN_RIGHT);
318 
319 	fColorsMenu = new BPopUpMenu("colors", true, false);
320 
321 	for (int32 i = 0; i < kColorSpaceCount; i++) {
322 		if ((fSupportedColorSpaces & (1 << i)) == 0)
323 			continue;
324 
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);
328 
329 		BMenuItem* item = new BMenuItem(kColorSpaces[i].label, message);
330 		if (kColorSpaces[i].space == screen.ColorSpace())
331 			fUserSelectedColorSpace = item;
332 
333 		fColorsMenu->AddItem(item);
334 	}
335 
336 	fColorsField = new BMenuField("ColorsMenu", B_TRANSLATE("Colors:"),
337 		fColorsMenu);
338 	fColorsField->SetAlignment(B_ALIGN_RIGHT);
339 
340 	fRefreshMenu = new BPopUpMenu("refresh rate", true, true);
341 
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];
349 	}
350 
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);
366 		}
367 
368 		for (int32 i = 0; i < kRefreshRateCount; ++i) {
369 			if (kRefreshRates[i] < min || kRefreshRates[i] > max)
370 				continue;
371 
372 			BString name;
373 			name << kRefreshRates[i] << " " << B_TRANSLATE("Hz");
374 
375 			BMessage *message = new BMessage(POP_REFRESH_MSG);
376 			message->AddFloat("refresh", kRefreshRates[i]);
377 
378 			fRefreshMenu->AddItem(new BMenuItem(name.String(), message));
379 		}
380 
381 		fOtherRefresh = new BMenuItem(B_TRANSLATE("Other" B_UTF8_ELLIPSIS),
382 			new BMessage(POP_OTHER_REFRESH_MSG));
383 		fRefreshMenu->AddItem(fOtherRefresh);
384 	}
385 
386 	fRefreshField = new BMenuField("RefreshMenu", B_TRANSLATE("Refresh rate:"),
387 		fRefreshMenu);
388 	fRefreshField->SetAlignment(B_ALIGN_RIGHT);
389 
390 	if (_IsVesa())
391 		fRefreshField->Hide();
392 
393 	// enlarged area for multi-monitor settings
394 	{
395 		bool dummy;
396 		uint32 dummy32;
397 		bool multiMonSupport;
398 		bool useLaptopPanelSupport;
399 		bool tvStandardSupport;
400 
401 		multiMonSupport = TestMultiMonSupport(&screen) == B_OK;
402 		useLaptopPanelSupport = GetUseLaptopPanel(&screen, &dummy) == B_OK;
403 		tvStandardSupport = GetTVStandard(&screen, &dummy32) == B_OK;
404 
405 		// even if there is no support, we still create all controls
406 		// to make sure we don't access NULL pointers later on
407 
408 		fCombineMenu = new BPopUpMenu("CombineDisplays",
409 			true, true);
410 
411 		for (int32 i = 0; i < kCombineModeCount; i++) {
412 			BMessage *message = new BMessage(POP_COMBINE_DISPLAYS_MSG);
413 			message->AddInt32("mode", kCombineModes[i].mode);
414 
415 			fCombineMenu->AddItem(new BMenuItem(kCombineModes[i].name,
416 				message));
417 		}
418 
419 		fCombineField = new BMenuField("CombineMenu",
420 			B_TRANSLATE("Combine displays:"), fCombineMenu);
421 		fCombineField->SetAlignment(B_ALIGN_RIGHT);
422 
423 		if (!multiMonSupport)
424 			fCombineField->Hide();
425 
426 		fSwapDisplaysMenu = new BPopUpMenu("SwapDisplays",
427 			true, true);
428 
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));
433 
434 		message = new BMessage(POP_SWAP_DISPLAYS_MSG);
435 		message->AddBool("swap", true);
436 		fSwapDisplaysMenu->AddItem(new BMenuItem(B_TRANSLATE("yes"), message));
437 
438 		fSwapDisplaysField = new BMenuField("SwapMenu",
439 			B_TRANSLATE("Swap displays:"), fSwapDisplaysMenu);
440 		fSwapDisplaysField->SetAlignment(B_ALIGN_RIGHT);
441 
442 		if (!multiMonSupport)
443 			fSwapDisplaysField->Hide();
444 
445 		fUseLaptopPanelMenu = new BPopUpMenu("UseLaptopPanel",
446 			true, true);
447 
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));
453 
454 		message = new BMessage(POP_USE_LAPTOP_PANEL_MSG);
455 		message->AddBool("use", true);
456 		fUseLaptopPanelMenu->AddItem(new BMenuItem(B_TRANSLATE("always"),
457 			message));
458 
459 		fUseLaptopPanelField = new BMenuField("UseLaptopPanel",
460 			B_TRANSLATE("Use laptop panel:"), fUseLaptopPanelMenu);
461 		fUseLaptopPanelField->SetAlignment(B_ALIGN_RIGHT);
462 
463 		if (!useLaptopPanelSupport)
464 			fUseLaptopPanelField->Hide();
465 
466 		fTVStandardMenu = new BPopUpMenu("TVStandard", true, true);
467 
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;
474 
475 			BString name = tv_standard_to_string(mode);
476 
477 			message = new BMessage(POP_TV_STANDARD_MSG);
478 			message->AddInt32("tv_standard", mode);
479 
480 			fTVStandardMenu->AddItem(new BMenuItem(name.String(), message));
481 		}
482 
483 		fTVStandardField = new BMenuField("tv standard",
484 			B_TRANSLATE("Video format:"), fTVStandardMenu);
485 		fTVStandardField->SetAlignment(B_ALIGN_RIGHT);
486 
487 		if (!tvStandardSupport || i == 0)
488 			fTVStandardField->Hide();
489 	}
490 
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();
508 
509 	fBrightnessSlider = new BSlider("brightness", "Brightness",
510 		NULL, 0, 255, B_HORIZONTAL);
511 
512 	status_t result = screen.GetBrightness(&fOriginalBrightness);
513 	if (result == B_OK) {
514 		fBrightnessSlider->SetModificationMessage(
515 			new BMessage(SLIDER_BRIGHTNESS_MSG));
516 		fBrightnessSlider->SetValue(fOriginalBrightness * 255);
517 	} else {
518 		// The driver does not support changing the brightness,
519 		// so hide the slider
520 		fBrightnessSlider->Hide();
521 
522 		fOriginalBrightness = -1;
523 	}
524 
525 	// TODO: we don't support getting the screen's preferred settings
526 	/* fDefaultsButton = new BButton(buttonRect, "DefaultsButton", "Defaults",
527 		new BMessage(BUTTON_DEFAULTS_MSG));*/
528 
529 	fApplyButton = new BButton("ApplyButton", B_TRANSLATE("Apply"),
530 		new BMessage(BUTTON_APPLY_MSG));
531 	fApplyButton->SetEnabled(false);
532 	BLayoutBuilder::Group<>(outerControlsView)
533 		.AddGlue()
534 		.AddGroup(B_HORIZONTAL)
535 			.AddGlue()
536 			.Add(fApplyButton);
537 
538 	fRevertButton = new BButton("RevertButton", B_TRANSLATE("Revert"),
539 		new BMessage(BUTTON_REVERT_MSG));
540 	fRevertButton->SetEnabled(false);
541 
542 	BLayoutBuilder::Group<>(this, B_VERTICAL, B_USE_DEFAULT_SPACING)
543 		.AddGroup(B_HORIZONTAL)
544 			.AddGroup(B_VERTICAL, 0, 1)
545 				.AddStrut(floorf(controlsBox->TopBorderOffset()) - 1)
546 				.Add(screenBox)
547 				.End()
548 			.AddGroup(B_VERTICAL, 0, 1)
549 				.Add(controlsBox, 2)
550 				.Add(fBrightnessSlider)
551 				.End()
552 			.End()
553 		.AddGroup(B_HORIZONTAL, B_USE_DEFAULT_SPACING)
554 			.Add(fRevertButton)
555 			.AddGlue()
556 			.End()
557 		.SetInsets(B_USE_WINDOW_SPACING);
558 
559 	_UpdateControls();
560 	_UpdateMonitor();
561 
562 	MoveOnScreen();
563 }
564 
565 
566 ScreenWindow::~ScreenWindow()
567 {
568 	delete fSettings;
569 }
570 
571 
572 bool
573 ScreenWindow::QuitRequested()
574 {
575 	fSettings->SetWindowFrame(Frame());
576 
577 	// Write mode of workspace 0 (the boot workspace) to the vesa settings file
578 	screen_mode vesaMode;
579 	if (fBootWorkspaceApplied && fScreenMode.Get(vesaMode, 0) == B_OK) {
580 		status_t status = _WriteVesaModeFile(vesaMode);
581 		if (status < B_OK) {
582 			BString warning = B_TRANSLATE("Could not write VESA mode settings"
583 				" file:\n\t");
584 			warning << strerror(status);
585 			BAlert* alert = new BAlert(B_TRANSLATE("Warning"),
586 				warning.String(), B_TRANSLATE("OK"), NULL,
587 				NULL, B_WIDTH_AS_USUAL, B_WARNING_ALERT);
588 			alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
589 			alert->Go();
590 		}
591 	}
592 
593 	be_app->PostMessage(B_QUIT_REQUESTED);
594 
595 	return BWindow::QuitRequested();
596 }
597 
598 
599 /*!	Update resolution list according to combine mode
600 	(some resolutions may not be combinable due to memory restrictions).
601 */
602 void
603 ScreenWindow::_CheckResolutionMenu()
604 {
605 	for (int32 i = 0; i < fResolutionMenu->CountItems(); i++)
606 		fResolutionMenu->ItemAt(i)->SetEnabled(false);
607 
608 	for (int32 i = 0; i < fScreenMode.CountModes(); i++) {
609 		screen_mode mode = fScreenMode.ModeAt(i);
610 		if (mode.combine != fSelected.combine)
611 			continue;
612 
613 		BString name;
614 		name << mode.width << " x " << mode.height;
615 
616 		BMenuItem *item = fResolutionMenu->FindItem(name.String());
617 		if (item != NULL)
618 			item->SetEnabled(true);
619 	}
620 }
621 
622 
623 /*!	Update color and refresh options according to current mode
624 	(a color space is made active if there is any mode with
625 	given resolution and this colour space; same applies for
626 	refresh rate, though "Other…" is always possible)
627 */
628 void
629 ScreenWindow::_CheckColorMenu()
630 {
631 	int32 supportsAnything = false;
632 	int32 index = 0;
633 
634 	for (int32 i = 0; i < kColorSpaceCount; i++) {
635 		if ((fSupportedColorSpaces & (1 << i)) == 0)
636 			continue;
637 
638 		bool supported = false;
639 
640 		for (int32 j = 0; j < fScreenMode.CountModes(); j++) {
641 			screen_mode mode = fScreenMode.ModeAt(j);
642 
643 			if (fSelected.width == mode.width
644 				&& fSelected.height == mode.height
645 				&& kColorSpaces[i].space == mode.space
646 				&& fSelected.combine == mode.combine) {
647 				supportsAnything = true;
648 				supported = true;
649 				break;
650 			}
651 		}
652 
653 		BMenuItem* item = fColorsMenu->ItemAt(index++);
654 		if (item)
655 			item->SetEnabled(supported);
656 	}
657 
658 	fColorsField->SetEnabled(supportsAnything);
659 
660 	if (!supportsAnything)
661 		return;
662 
663 	// Make sure a valid item is selected
664 
665 	BMenuItem* item = fColorsMenu->FindMarked();
666 	bool changed = false;
667 
668 	if (item != fUserSelectedColorSpace) {
669 		if (fUserSelectedColorSpace != NULL
670 			&& fUserSelectedColorSpace->IsEnabled()) {
671 			fUserSelectedColorSpace->SetMarked(true);
672 			item = fUserSelectedColorSpace;
673 			changed = true;
674 		}
675 	}
676 	if (item != NULL && !item->IsEnabled()) {
677 		// find the next best item
678 		int32 index = fColorsMenu->IndexOf(item);
679 		bool found = false;
680 
681 		for (int32 i = index + 1; i < fColorsMenu->CountItems(); i++) {
682 			item = fColorsMenu->ItemAt(i);
683 			if (item->IsEnabled()) {
684 				found = true;
685 				break;
686 			}
687 		}
688 		if (!found) {
689 			// search backwards as well
690 			for (int32 i = index - 1; i >= 0; i--) {
691 				item = fColorsMenu->ItemAt(i);
692 				if (item->IsEnabled())
693 					break;
694 			}
695 		}
696 
697 		item->SetMarked(true);
698 		changed = true;
699 	}
700 
701 	if (changed) {
702 		// Update selected space
703 
704 		BMessage* message = item->Message();
705 		int32 space;
706 		if (message->FindInt32("space", &space) == B_OK) {
707 			fSelected.space = (color_space)space;
708 			_UpdateColorLabel();
709 		}
710 	}
711 }
712 
713 
714 /*!	Enable/disable refresh options according to current mode. */
715 void
716 ScreenWindow::_CheckRefreshMenu()
717 {
718 	float min, max;
719 	if (fScreenMode.GetRefreshLimits(fSelected, min, max) != B_OK || min == max)
720 		return;
721 
722 	for (int32 i = fRefreshMenu->CountItems(); i-- > 0;) {
723 		BMenuItem* item = fRefreshMenu->ItemAt(i);
724 		BMessage* message = item->Message();
725 		float refresh;
726 		if (message != NULL && message->FindFloat("refresh", &refresh) == B_OK)
727 			item->SetEnabled(refresh >= min && refresh <= max);
728 	}
729 }
730 
731 
732 /*!	Activate appropriate menu item according to selected refresh rate */
733 void
734 ScreenWindow::_UpdateRefreshControl()
735 {
736 	for (int32 i = 0; i < fRefreshMenu->CountItems(); i++) {
737 		BMenuItem* item = fRefreshMenu->ItemAt(i);
738 		if (item->Message()->FindFloat("refresh") == fSelected.refresh) {
739 			item->SetMarked(true);
740 			// "Other" items only contains a refresh rate when active
741 			fOtherRefresh->SetLabel(B_TRANSLATE("Other" B_UTF8_ELLIPSIS));
742 			return;
743 		}
744 	}
745 
746 	// this is a non-standard refresh rate
747 	if (fOtherRefresh != NULL) {
748 		fOtherRefresh->Message()->ReplaceFloat("refresh", fSelected.refresh);
749 		fOtherRefresh->SetMarked(true);
750 
751 		BString string;
752 		refresh_rate_to_string(fSelected.refresh, string);
753 		fRefreshMenu->Superitem()->SetLabel(string.String());
754 
755 		string.Append(B_TRANSLATE("/other" B_UTF8_ELLIPSIS));
756 		fOtherRefresh->SetLabel(string.String());
757 	}
758 }
759 
760 
761 void
762 ScreenWindow::_UpdateMonitorView()
763 {
764 	BMessage updateMessage(UPDATE_DESKTOP_MSG);
765 	updateMessage.AddInt32("width", fSelected.width);
766 	updateMessage.AddInt32("height", fSelected.height);
767 
768 	PostMessage(&updateMessage, fMonitorView);
769 }
770 
771 
772 void
773 ScreenWindow::_UpdateControls()
774 {
775 	_UpdateWorkspaceButtons();
776 
777 	BMenuItem* item = fSwapDisplaysMenu->ItemAt((int32)fSelected.swap_displays);
778 	if (item != NULL && !item->IsMarked())
779 		item->SetMarked(true);
780 
781 	item = fUseLaptopPanelMenu->ItemAt((int32)fSelected.use_laptop_panel);
782 	if (item != NULL && !item->IsMarked())
783 		item->SetMarked(true);
784 
785 	for (int32 i = 0; i < fTVStandardMenu->CountItems(); i++) {
786 		item = fTVStandardMenu->ItemAt(i);
787 
788 		uint32 tvStandard;
789 		item->Message()->FindInt32("tv_standard", (int32 *)&tvStandard);
790 		if (tvStandard == fSelected.tv_standard) {
791 			if (!item->IsMarked())
792 				item->SetMarked(true);
793 			break;
794 		}
795 	}
796 
797 	_CheckResolutionMenu();
798 	_CheckColorMenu();
799 	_CheckRefreshMenu();
800 
801 	BString string;
802 	resolution_to_string(fSelected, string);
803 	item = fResolutionMenu->FindItem(string.String());
804 
805 	if (item != NULL) {
806 		if (!item->IsMarked())
807 			item->SetMarked(true);
808 	} else {
809 		// this is bad luck - if mode has been set via screen references,
810 		// this case cannot occur; there are three possible solutions:
811 		// 1. add a new resolution to list
812 		//    - we had to remove it as soon as a "valid" one is selected
813 		//    - we don't know which frequencies/bit depths are supported
814 		//    - as long as we haven't the GMT formula to create
815 		//      parameters for any resolution given, we cannot
816 		//      really set current mode - it's just not in the list
817 		// 2. choose nearest resolution
818 		//    - probably a good idea, but implies coding and testing
819 		// 3. choose lowest resolution
820 		//    - do you really think we are so lazy? yes, we are
821 		item = fResolutionMenu->ItemAt(0);
822 		if (item)
823 			item->SetMarked(true);
824 
825 		// okay - at least we set menu label to active resolution
826 		fResolutionMenu->Superitem()->SetLabel(string.String());
827 	}
828 
829 	// mark active combine mode
830 	for (int32 i = 0; i < kCombineModeCount; i++) {
831 		if (kCombineModes[i].mode == fSelected.combine) {
832 			item = fCombineMenu->ItemAt(i);
833 			if (item != NULL && !item->IsMarked())
834 				item->SetMarked(true);
835 			break;
836 		}
837 	}
838 
839 	item = fColorsMenu->ItemAt(0);
840 
841 	for (int32 i = 0, index = 0; i <  kColorSpaceCount; i++) {
842 		if ((fSupportedColorSpaces & (1 << i)) == 0)
843 			continue;
844 
845 		if (kColorSpaces[i].space == fSelected.space) {
846 			item = fColorsMenu->ItemAt(index);
847 			break;
848 		}
849 
850 		index++;
851 	}
852 
853 	if (item != NULL && !item->IsMarked())
854 		item->SetMarked(true);
855 
856 	_UpdateColorLabel();
857 	_UpdateMonitorView();
858 	_UpdateRefreshControl();
859 
860 	_CheckApplyEnabled();
861 }
862 
863 
864 /*! Reflect active mode in chosen settings */
865 void
866 ScreenWindow::_UpdateActiveMode()
867 {
868 	_UpdateActiveMode(current_workspace());
869 }
870 
871 
872 void
873 ScreenWindow::_UpdateActiveMode(int32 workspace)
874 {
875 	// Usually, this function gets called after a mode
876 	// has been set manually; still, as the graphics driver
877 	// is free to fiddle with mode passed, we better ask
878 	// what kind of mode we actually got
879 	if (fScreenMode.Get(fActive, workspace) == B_OK) {
880 		fSelected = fActive;
881 
882 		_UpdateMonitor();
883 		_BuildSupportedColorSpaces();
884 		_UpdateControls();
885 	}
886 }
887 
888 
889 void
890 ScreenWindow::_UpdateWorkspaceButtons()
891 {
892 	uint32 columns;
893 	uint32 rows;
894 	BPrivate::get_workspaces_layout(&columns, &rows);
895 
896 	// Set the max values enabling/disabling the up/down arrows
897 
898 	if (rows == 1)
899 		fColumnsControl->SetMaxValue(32);
900 	else if (rows == 2)
901 		fColumnsControl->SetMaxValue(16);
902 	else if (rows <= 4)
903 		fColumnsControl->SetMaxValue(8);
904 	else if (rows <= 8)
905 		fColumnsControl->SetMaxValue(4);
906 	else if (rows <= 16)
907 		fColumnsControl->SetMaxValue(2);
908 	else if (rows <= 32)
909 		fColumnsControl->SetMaxValue(1);
910 
911 	if (columns == 1)
912 		fRowsControl->SetMaxValue(32);
913 	else if (columns == 2)
914 		fRowsControl->SetMaxValue(16);
915 	else if (columns <= 4)
916 		fRowsControl->SetMaxValue(8);
917 	else if (columns <= 8)
918 		fRowsControl->SetMaxValue(4);
919 	else if (columns <= 16)
920 		fRowsControl->SetMaxValue(2);
921 	else if (columns <= 32)
922 		fRowsControl->SetMaxValue(1);
923 }
924 
925 
926 void
927 ScreenWindow::ScreenChanged(BRect frame, color_space mode)
928 {
929 	// move window on screen, if necessary
930 	if (frame.right <= Frame().right
931 		&& frame.bottom <= Frame().bottom) {
932 		MoveTo((frame.Width() - Frame().Width()) / 2,
933 			(frame.Height() - Frame().Height()) / 2);
934 	}
935 }
936 
937 
938 void
939 ScreenWindow::WorkspaceActivated(int32 workspace, bool state)
940 {
941 	if (fScreenMode.GetOriginalMode(fOriginal, workspace) == B_OK) {
942 		_UpdateActiveMode(workspace);
943 
944 		BMessage message(UPDATE_DESKTOP_COLOR_MSG);
945 		PostMessage(&message, fMonitorView);
946 	}
947 }
948 
949 
950 void
951 ScreenWindow::MessageReceived(BMessage* message)
952 {
953 	switch (message->what) {
954 		case WORKSPACE_CHECK_MSG:
955 			_CheckApplyEnabled();
956 			break;
957 
958 		case kMsgWorkspaceColumnsChanged:
959 		{
960 			uint32 newColumns = (uint32)fColumnsControl->Value();
961 
962 			uint32 rows;
963 			BPrivate::get_workspaces_layout(NULL, &rows);
964 			BPrivate::set_workspaces_layout(newColumns, rows);
965 
966 			_UpdateWorkspaceButtons();
967 			fRowsControl->SetValue(rows);
968 				// enables/disables up/down arrows
969 			_CheckApplyEnabled();
970 
971 			break;
972 		}
973 
974 		case kMsgWorkspaceRowsChanged:
975 		{
976 			uint32 newRows = (uint32)fRowsControl->Value();
977 
978 			uint32 columns;
979 			BPrivate::get_workspaces_layout(&columns, NULL);
980 			BPrivate::set_workspaces_layout(columns, newRows);
981 
982 			_UpdateWorkspaceButtons();
983 			fColumnsControl->SetValue(columns);
984 				// enables/disables up/down arrows
985 			_CheckApplyEnabled();
986 			break;
987 		}
988 
989 		case POP_RESOLUTION_MSG:
990 		{
991 			message->FindInt32("width", &fSelected.width);
992 			message->FindInt32("height", &fSelected.height);
993 
994 			_CheckColorMenu();
995 			_CheckRefreshMenu();
996 
997 			_UpdateMonitorView();
998 			_UpdateRefreshControl();
999 
1000 			_CheckApplyEnabled();
1001 			break;
1002 		}
1003 
1004 		case POP_COLORS_MSG:
1005 		{
1006 			int32 space;
1007 			if (message->FindInt32("space", &space) != B_OK)
1008 				break;
1009 
1010 			int32 index;
1011 			if (message->FindInt32("index", &index) == B_OK
1012 				&& fColorsMenu->ItemAt(index) != NULL)
1013 				fUserSelectedColorSpace = fColorsMenu->ItemAt(index);
1014 
1015 			fSelected.space = (color_space)space;
1016 			_UpdateColorLabel();
1017 
1018 			_CheckApplyEnabled();
1019 			break;
1020 		}
1021 
1022 		case POP_REFRESH_MSG:
1023 		{
1024 			message->FindFloat("refresh", &fSelected.refresh);
1025 			fOtherRefresh->SetLabel(B_TRANSLATE("Other" B_UTF8_ELLIPSIS));
1026 				// revert "Other…" label - it might have a refresh rate prefix
1027 
1028 			_CheckApplyEnabled();
1029 			break;
1030 		}
1031 
1032 		case POP_OTHER_REFRESH_MSG:
1033 		{
1034 			// make sure menu shows something useful
1035 			_UpdateRefreshControl();
1036 
1037 			float min = 0, max = 999;
1038 			fScreenMode.GetRefreshLimits(fSelected, min, max);
1039 			if (min < gMinRefresh)
1040 				min = gMinRefresh;
1041 			if (max > gMaxRefresh)
1042 				max = gMaxRefresh;
1043 
1044 			monitor_info info;
1045 			if (fScreenMode.GetMonitorInfo(info) == B_OK) {
1046 				min = max_c(info.min_vertical_frequency, min);
1047 				max = min_c(info.max_vertical_frequency, max);
1048 			}
1049 
1050 			RefreshWindow *fRefreshWindow = new RefreshWindow(
1051 				fRefreshField->ConvertToScreen(B_ORIGIN), fSelected.refresh,
1052 				min, max);
1053 			fRefreshWindow->Show();
1054 			break;
1055 		}
1056 
1057 		case SET_CUSTOM_REFRESH_MSG:
1058 		{
1059 			// user pressed "done" in "Other…" refresh dialog;
1060 			// select the refresh rate chosen
1061 			message->FindFloat("refresh", &fSelected.refresh);
1062 
1063 			_UpdateRefreshControl();
1064 			_CheckApplyEnabled();
1065 			break;
1066 		}
1067 
1068 		case POP_COMBINE_DISPLAYS_MSG:
1069 		{
1070 			// new combine mode has bee chosen
1071 			int32 mode;
1072 			if (message->FindInt32("mode", &mode) == B_OK)
1073 				fSelected.combine = (combine_mode)mode;
1074 
1075 			_CheckResolutionMenu();
1076 			_CheckApplyEnabled();
1077 			break;
1078 		}
1079 
1080 		case POP_SWAP_DISPLAYS_MSG:
1081 			message->FindBool("swap", &fSelected.swap_displays);
1082 			_CheckApplyEnabled();
1083 			break;
1084 
1085 		case POP_USE_LAPTOP_PANEL_MSG:
1086 			message->FindBool("use", &fSelected.use_laptop_panel);
1087 			_CheckApplyEnabled();
1088 			break;
1089 
1090 		case POP_TV_STANDARD_MSG:
1091 			message->FindInt32("tv_standard", (int32 *)&fSelected.tv_standard);
1092 			_CheckApplyEnabled();
1093 			break;
1094 
1095 		case BUTTON_LAUNCH_BACKGROUNDS_MSG:
1096 			if (be_roster->Launch(kBackgroundsSignature) == B_ALREADY_RUNNING) {
1097 				app_info info;
1098 				be_roster->GetAppInfo(kBackgroundsSignature, &info);
1099 				be_roster->ActivateApp(info.team);
1100 			}
1101 			break;
1102 
1103 		case BUTTON_DEFAULTS_MSG:
1104 		{
1105 			// TODO: get preferred settings of screen
1106 			fSelected.width = 640;
1107 			fSelected.height = 480;
1108 			fSelected.space = B_CMAP8;
1109 			fSelected.refresh = 60.0;
1110 			fSelected.combine = kCombineDisable;
1111 			fSelected.swap_displays = false;
1112 			fSelected.use_laptop_panel = false;
1113 			fSelected.tv_standard = 0;
1114 
1115 			// TODO: workspace defaults
1116 
1117 			_UpdateControls();
1118 			break;
1119 		}
1120 
1121 		case BUTTON_UNDO_MSG:
1122 			fUndoScreenMode.Revert();
1123 			_UpdateActiveMode();
1124 			break;
1125 
1126 		case BUTTON_REVERT_MSG:
1127 		{
1128 			fModified = false;
1129 			fBootWorkspaceApplied = false;
1130 
1131 			// ScreenMode::Revert() assumes that we first set the correct
1132 			// number of workspaces
1133 
1134 			BPrivate::set_workspaces_layout(fOriginalWorkspacesColumns,
1135 				fOriginalWorkspacesRows);
1136 			_UpdateWorkspaceButtons();
1137 
1138 			fScreenMode.Revert();
1139 			_UpdateActiveMode();
1140 
1141 			BScreen screen(this);
1142 			screen.SetBrightness(fOriginalBrightness);
1143 			fBrightnessSlider->SetValue(fOriginalBrightness * 255);
1144 			break;
1145 		}
1146 
1147 		case BUTTON_APPLY_MSG:
1148 			_Apply();
1149 			break;
1150 
1151 		case MAKE_INITIAL_MSG:
1152 			// user pressed "keep" in confirmation dialog
1153 			fModified = true;
1154 			_UpdateActiveMode();
1155 			break;
1156 
1157 		case UPDATE_DESKTOP_COLOR_MSG:
1158 			PostMessage(message, fMonitorView);
1159 			break;
1160 
1161 		case SLIDER_BRIGHTNESS_MSG:
1162 		{
1163 			BScreen screen(this);
1164 			screen.SetBrightness(message->FindInt32("be:value") / 255.f);
1165 			_CheckApplyEnabled();
1166 			break;
1167 		}
1168 
1169 		default:
1170 			BWindow::MessageReceived(message);
1171 	}
1172 }
1173 
1174 
1175 status_t
1176 ScreenWindow::_WriteVesaModeFile(const screen_mode& mode) const
1177 {
1178 	BPath path;
1179 	status_t status = find_directory(B_USER_SETTINGS_DIRECTORY, &path, true);
1180 	if (status < B_OK)
1181 		return status;
1182 
1183 	path.Append("kernel/drivers");
1184 	status = create_directory(path.Path(), 0755);
1185 	if (status < B_OK)
1186 		return status;
1187 
1188 	path.Append("vesa");
1189 	BFile file;
1190 	status = file.SetTo(path.Path(), B_CREATE_FILE | B_WRITE_ONLY | B_ERASE_FILE);
1191 	if (status < B_OK)
1192 		return status;
1193 
1194 	char buffer[256];
1195 	snprintf(buffer, sizeof(buffer), "mode %" B_PRId32 " %" B_PRId32 " %"
1196 		B_PRId32 "\n", mode.width, mode.height, mode.BitsPerPixel());
1197 
1198 	ssize_t bytesWritten = file.Write(buffer, strlen(buffer));
1199 	if (bytesWritten < B_OK)
1200 		return bytesWritten;
1201 
1202 	return B_OK;
1203 }
1204 
1205 
1206 void
1207 ScreenWindow::_BuildSupportedColorSpaces()
1208 {
1209 	fSupportedColorSpaces = 0;
1210 
1211 	for (int32 i = 0; i < kColorSpaceCount; i++) {
1212 		for (int32 j = 0; j < fScreenMode.CountModes(); j++) {
1213 			if (fScreenMode.ModeAt(j).space == kColorSpaces[i].space) {
1214 				fSupportedColorSpaces |= 1 << i;
1215 				break;
1216 			}
1217 		}
1218 	}
1219 }
1220 
1221 
1222 void
1223 ScreenWindow::_CheckApplyEnabled()
1224 {
1225 	bool applyEnabled = true;
1226 
1227 	if (fSelected == fActive) {
1228 		applyEnabled = false;
1229 		if (fAllWorkspacesItem->IsMarked()) {
1230 			screen_mode screenMode;
1231 			const int32 workspaceCount = count_workspaces();
1232 			for (int32 i = 0; i < workspaceCount; i++) {
1233 				fScreenMode.Get(screenMode, i);
1234 				if (screenMode != fSelected) {
1235 					applyEnabled = true;
1236 					break;
1237 				}
1238 			}
1239 		}
1240 	}
1241 
1242 	fApplyButton->SetEnabled(applyEnabled);
1243 
1244 	uint32 columns;
1245 	uint32 rows;
1246 	BPrivate::get_workspaces_layout(&columns, &rows);
1247 
1248 	BScreen screen(this);
1249 	float brightness = -1;
1250 	screen.GetBrightness(&brightness);
1251 
1252 	fRevertButton->SetEnabled(columns != fOriginalWorkspacesColumns
1253 		|| rows != fOriginalWorkspacesRows
1254 		|| brightness != fOriginalBrightness
1255 		|| fSelected != fOriginal);
1256 }
1257 
1258 
1259 void
1260 ScreenWindow::_UpdateOriginal()
1261 {
1262 	BPrivate::get_workspaces_layout(&fOriginalWorkspacesColumns,
1263 		&fOriginalWorkspacesRows);
1264 
1265 	fScreenMode.Get(fOriginal);
1266 	fScreenMode.UpdateOriginalModes();
1267 }
1268 
1269 
1270 void
1271 ScreenWindow::_UpdateMonitor()
1272 {
1273 	monitor_info info;
1274 	float diagonalInches;
1275 	status_t status = fScreenMode.GetMonitorInfo(info, &diagonalInches);
1276 	if (status == B_OK) {
1277 		char text[512];
1278 		snprintf(text, sizeof(text), "%s%s%s %g\"", info.vendor,
1279 			info.name[0] ? " " : "", info.name, diagonalInches);
1280 
1281 		fMonitorInfo->SetText(text);
1282 
1283 		if (fMonitorInfo->IsHidden(fMonitorInfo))
1284 			fMonitorInfo->Show();
1285 	} else {
1286 		if (!fMonitorInfo->IsHidden(fMonitorInfo))
1287 			fMonitorInfo->Hide();
1288 	}
1289 
1290 	// Add info about the graphics device
1291 
1292 	accelerant_device_info deviceInfo;
1293 
1294 	if (fScreenMode.GetDeviceInfo(deviceInfo) == B_OK) {
1295 		BString deviceString;
1296 
1297 		if (deviceInfo.name[0] && deviceInfo.chipset[0]) {
1298 			deviceString.SetToFormat("%s (%s)", deviceInfo.name,
1299 				deviceInfo.chipset);
1300 		} else if (deviceInfo.name[0] || deviceInfo.chipset[0]) {
1301 			deviceString
1302 				= deviceInfo.name[0] ? deviceInfo.name : deviceInfo.chipset;
1303 		}
1304 
1305 		fDeviceInfo->SetText(deviceString);
1306 	}
1307 
1308 
1309 	char text[512];
1310 	size_t length = 0;
1311 	text[0] = 0;
1312 
1313 	if (status == B_OK) {
1314 		if (info.min_horizontal_frequency != 0
1315 			&& info.min_vertical_frequency != 0
1316 			&& info.max_pixel_clock != 0) {
1317 			length = snprintf(text, sizeof(text),
1318 				B_TRANSLATE("Horizonal frequency:\t%lu - %lu kHz\n"
1319 				"Vertical frequency:\t%lu - %lu Hz\n\n"
1320 				"Maximum pixel clock:\t%g MHz"),
1321 				info.min_horizontal_frequency, info.max_horizontal_frequency,
1322 				info.min_vertical_frequency, info.max_vertical_frequency,
1323 				info.max_pixel_clock / 1000.0);
1324 		}
1325 		if (info.serial_number[0] && length < sizeof(text)) {
1326 			length += snprintf(text + length, sizeof(text) - length,
1327 				B_TRANSLATE("%sSerial no.: %s"), length ? "\n\n" : "",
1328 				info.serial_number);
1329 			if (info.produced.week != 0 && info.produced.year != 0
1330 				&& length < sizeof(text)) {
1331 				length += snprintf(text + length, sizeof(text) - length,
1332 					" (%u/%u)", info.produced.week, info.produced.year);
1333 			}
1334 		}
1335 	}
1336 
1337 	if (text[0])
1338 		fMonitorView->SetToolTip(text);
1339 }
1340 
1341 
1342 void
1343 ScreenWindow::_UpdateColorLabel()
1344 {
1345 	BString string;
1346 	string << fSelected.BitsPerPixel() << " " << B_TRANSLATE("bits/pixel");
1347 	fColorsMenu->Superitem()->SetLabel(string.String());
1348 }
1349 
1350 
1351 void
1352 ScreenWindow::_Apply()
1353 {
1354 	// make checkpoint, so we can undo these changes
1355 	fUndoScreenMode.UpdateOriginalModes();
1356 
1357 	status_t status = fScreenMode.Set(fSelected);
1358 	if (status == B_OK) {
1359 		// use the mode that has eventually been set and
1360 		// thus we know to be working; it can differ from
1361 		// the mode selected by user due to hardware limitation
1362 		display_mode newMode;
1363 		BScreen screen(this);
1364 		screen.GetMode(&newMode);
1365 
1366 		if (fAllWorkspacesItem->IsMarked()) {
1367 			int32 originatingWorkspace = current_workspace();
1368 			const int32 workspaceCount = count_workspaces();
1369 			for (int32 i = 0; i < workspaceCount; i++) {
1370 				if (i != originatingWorkspace)
1371 					screen.SetMode(i, &newMode, true);
1372 			}
1373 			fBootWorkspaceApplied = true;
1374 		} else {
1375 			if (current_workspace() == 0)
1376 				fBootWorkspaceApplied = true;
1377 		}
1378 
1379 		fActive = fSelected;
1380 
1381 		// TODO: only show alert when this is an unknown mode
1382 		BAlert* window = new AlertWindow(this);
1383 		window->Go(NULL);
1384 	} else {
1385 		char message[256];
1386 		snprintf(message, sizeof(message),
1387 			B_TRANSLATE("The screen mode could not be set:\n\t%s\n"),
1388 			screen_errors(status));
1389 		BAlert* alert = new BAlert(B_TRANSLATE("Warning"), message,
1390 			B_TRANSLATE("OK"), NULL, NULL,
1391 			B_WIDTH_AS_USUAL, B_WARNING_ALERT);
1392 		alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
1393 		alert->Go();
1394 	}
1395 }
1396