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
74const char* kBackgroundsSignature = "application/x-vnd.Haiku-Backgrounds";
75
76// list of officially supported colour spaces
77static 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};
88static const int32 kColorSpaceCount
89	= sizeof(kColorSpaces) / sizeof(kColorSpaces[0]);
90
91// list of standard refresh rates
92static const int32 kRefreshRates[] = { 60, 70, 72, 75, 80, 85, 95, 100 };
93static const int32 kRefreshRateCount
94	= sizeof(kRefreshRates) / sizeof(kRefreshRates[0]);
95
96// list of combine modes
97static 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};
105static const int32 kCombineModeCount
106	= sizeof(kCombineModes) / sizeof(kCombineModes[0]);
107
108
109static BString
110tv_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
134static void
135resolution_to_string(screen_mode& mode, BString &string)
136{
137	string << mode.width << " x " << mode.height;
138}
139
140
141static void
142refresh_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
154static const char*
155screen_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
171ScreenWindow::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
566ScreenWindow::~ScreenWindow()
567{
568	delete fSettings;
569}
570
571
572bool
573ScreenWindow::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*/
602void
603ScreenWindow::_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*/
628void
629ScreenWindow::_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. */
715void
716ScreenWindow::_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 */
733void
734ScreenWindow::_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			if (fOtherRefresh != NULL)
742				fOtherRefresh->SetLabel(B_TRANSLATE("Other" B_UTF8_ELLIPSIS));
743			return;
744		}
745	}
746
747	// this is a non-standard refresh rate
748	if (fOtherRefresh != NULL) {
749		fOtherRefresh->Message()->ReplaceFloat("refresh", fSelected.refresh);
750		fOtherRefresh->SetMarked(true);
751
752		BString string;
753		refresh_rate_to_string(fSelected.refresh, string);
754		fRefreshMenu->Superitem()->SetLabel(string.String());
755
756		string.Append(B_TRANSLATE("/other" B_UTF8_ELLIPSIS));
757		fOtherRefresh->SetLabel(string.String());
758	}
759}
760
761
762void
763ScreenWindow::_UpdateMonitorView()
764{
765	BMessage updateMessage(UPDATE_DESKTOP_MSG);
766	updateMessage.AddInt32("width", fSelected.width);
767	updateMessage.AddInt32("height", fSelected.height);
768
769	PostMessage(&updateMessage, fMonitorView);
770}
771
772
773void
774ScreenWindow::_UpdateControls()
775{
776	_UpdateWorkspaceButtons();
777
778	BMenuItem* item = fSwapDisplaysMenu->ItemAt((int32)fSelected.swap_displays);
779	if (item != NULL && !item->IsMarked())
780		item->SetMarked(true);
781
782	item = fUseLaptopPanelMenu->ItemAt((int32)fSelected.use_laptop_panel);
783	if (item != NULL && !item->IsMarked())
784		item->SetMarked(true);
785
786	for (int32 i = 0; i < fTVStandardMenu->CountItems(); i++) {
787		item = fTVStandardMenu->ItemAt(i);
788
789		uint32 tvStandard;
790		item->Message()->FindInt32("tv_standard", (int32 *)&tvStandard);
791		if (tvStandard == fSelected.tv_standard) {
792			if (!item->IsMarked())
793				item->SetMarked(true);
794			break;
795		}
796	}
797
798	_CheckResolutionMenu();
799	_CheckColorMenu();
800	_CheckRefreshMenu();
801
802	BString string;
803	resolution_to_string(fSelected, string);
804	item = fResolutionMenu->FindItem(string.String());
805
806	if (item != NULL) {
807		if (!item->IsMarked())
808			item->SetMarked(true);
809	} else {
810		// this is bad luck - if mode has been set via screen references,
811		// this case cannot occur; there are three possible solutions:
812		// 1. add a new resolution to list
813		//    - we had to remove it as soon as a "valid" one is selected
814		//    - we don't know which frequencies/bit depths are supported
815		//    - as long as we haven't the GMT formula to create
816		//      parameters for any resolution given, we cannot
817		//      really set current mode - it's just not in the list
818		// 2. choose nearest resolution
819		//    - probably a good idea, but implies coding and testing
820		// 3. choose lowest resolution
821		//    - do you really think we are so lazy? yes, we are
822		item = fResolutionMenu->ItemAt(0);
823		if (item)
824			item->SetMarked(true);
825
826		// okay - at least we set menu label to active resolution
827		fResolutionMenu->Superitem()->SetLabel(string.String());
828	}
829
830	// mark active combine mode
831	for (int32 i = 0; i < kCombineModeCount; i++) {
832		if (kCombineModes[i].mode == fSelected.combine) {
833			item = fCombineMenu->ItemAt(i);
834			if (item != NULL && !item->IsMarked())
835				item->SetMarked(true);
836			break;
837		}
838	}
839
840	item = fColorsMenu->ItemAt(0);
841
842	for (int32 i = 0, index = 0; i <  kColorSpaceCount; i++) {
843		if ((fSupportedColorSpaces & (1 << i)) == 0)
844			continue;
845
846		if (kColorSpaces[i].space == fSelected.space) {
847			item = fColorsMenu->ItemAt(index);
848			break;
849		}
850
851		index++;
852	}
853
854	if (item != NULL && !item->IsMarked())
855		item->SetMarked(true);
856
857	_UpdateColorLabel();
858	_UpdateMonitorView();
859	_UpdateRefreshControl();
860
861	_CheckApplyEnabled();
862}
863
864
865/*! Reflect active mode in chosen settings */
866void
867ScreenWindow::_UpdateActiveMode()
868{
869	_UpdateActiveMode(current_workspace());
870}
871
872
873void
874ScreenWindow::_UpdateActiveMode(int32 workspace)
875{
876	// Usually, this function gets called after a mode
877	// has been set manually; still, as the graphics driver
878	// is free to fiddle with mode passed, we better ask
879	// what kind of mode we actually got
880	if (fScreenMode.Get(fActive, workspace) == B_OK) {
881		fSelected = fActive;
882
883		_UpdateMonitor();
884		_BuildSupportedColorSpaces();
885		_UpdateControls();
886	}
887}
888
889
890void
891ScreenWindow::_UpdateWorkspaceButtons()
892{
893	uint32 columns;
894	uint32 rows;
895	BPrivate::get_workspaces_layout(&columns, &rows);
896
897	// Set the max values enabling/disabling the up/down arrows
898
899	if (rows == 1)
900		fColumnsControl->SetMaxValue(32);
901	else if (rows == 2)
902		fColumnsControl->SetMaxValue(16);
903	else if (rows <= 4)
904		fColumnsControl->SetMaxValue(8);
905	else if (rows <= 8)
906		fColumnsControl->SetMaxValue(4);
907	else if (rows <= 16)
908		fColumnsControl->SetMaxValue(2);
909	else if (rows <= 32)
910		fColumnsControl->SetMaxValue(1);
911
912	if (columns == 1)
913		fRowsControl->SetMaxValue(32);
914	else if (columns == 2)
915		fRowsControl->SetMaxValue(16);
916	else if (columns <= 4)
917		fRowsControl->SetMaxValue(8);
918	else if (columns <= 8)
919		fRowsControl->SetMaxValue(4);
920	else if (columns <= 16)
921		fRowsControl->SetMaxValue(2);
922	else if (columns <= 32)
923		fRowsControl->SetMaxValue(1);
924}
925
926
927void
928ScreenWindow::ScreenChanged(BRect frame, color_space mode)
929{
930	// move window on screen, if necessary
931	if (frame.right <= Frame().right
932		&& frame.bottom <= Frame().bottom) {
933		MoveTo((frame.Width() - Frame().Width()) / 2,
934			(frame.Height() - Frame().Height()) / 2);
935	}
936}
937
938
939void
940ScreenWindow::WorkspaceActivated(int32 workspace, bool state)
941{
942	if (fScreenMode.GetOriginalMode(fOriginal, workspace) == B_OK) {
943		_UpdateActiveMode(workspace);
944
945		BMessage message(UPDATE_DESKTOP_COLOR_MSG);
946		PostMessage(&message, fMonitorView);
947	}
948}
949
950
951void
952ScreenWindow::MessageReceived(BMessage* message)
953{
954	switch (message->what) {
955		case WORKSPACE_CHECK_MSG:
956			_CheckApplyEnabled();
957			break;
958
959		case kMsgWorkspaceColumnsChanged:
960		{
961			uint32 newColumns = (uint32)fColumnsControl->Value();
962
963			uint32 rows;
964			BPrivate::get_workspaces_layout(NULL, &rows);
965			BPrivate::set_workspaces_layout(newColumns, rows);
966
967			_UpdateWorkspaceButtons();
968			fRowsControl->SetValue(rows);
969				// enables/disables up/down arrows
970			_CheckApplyEnabled();
971
972			break;
973		}
974
975		case kMsgWorkspaceRowsChanged:
976		{
977			uint32 newRows = (uint32)fRowsControl->Value();
978
979			uint32 columns;
980			BPrivate::get_workspaces_layout(&columns, NULL);
981			BPrivate::set_workspaces_layout(columns, newRows);
982
983			_UpdateWorkspaceButtons();
984			fColumnsControl->SetValue(columns);
985				// enables/disables up/down arrows
986			_CheckApplyEnabled();
987			break;
988		}
989
990		case POP_RESOLUTION_MSG:
991		{
992			message->FindInt32("width", &fSelected.width);
993			message->FindInt32("height", &fSelected.height);
994
995			_CheckColorMenu();
996			_CheckRefreshMenu();
997
998			_UpdateMonitorView();
999			_UpdateRefreshControl();
1000
1001			_CheckApplyEnabled();
1002			break;
1003		}
1004
1005		case POP_COLORS_MSG:
1006		{
1007			int32 space;
1008			if (message->FindInt32("space", &space) != B_OK)
1009				break;
1010
1011			int32 index;
1012			if (message->FindInt32("index", &index) == B_OK
1013				&& fColorsMenu->ItemAt(index) != NULL)
1014				fUserSelectedColorSpace = fColorsMenu->ItemAt(index);
1015
1016			fSelected.space = (color_space)space;
1017			_UpdateColorLabel();
1018
1019			_CheckApplyEnabled();
1020			break;
1021		}
1022
1023		case POP_REFRESH_MSG:
1024		{
1025			message->FindFloat("refresh", &fSelected.refresh);
1026			fOtherRefresh->SetLabel(B_TRANSLATE("Other" B_UTF8_ELLIPSIS));
1027				// revert "Other���" label - it might have a refresh rate prefix
1028
1029			_CheckApplyEnabled();
1030			break;
1031		}
1032
1033		case POP_OTHER_REFRESH_MSG:
1034		{
1035			// make sure menu shows something useful
1036			_UpdateRefreshControl();
1037
1038			float min = 0, max = 999;
1039			fScreenMode.GetRefreshLimits(fSelected, min, max);
1040			if (min < gMinRefresh)
1041				min = gMinRefresh;
1042			if (max > gMaxRefresh)
1043				max = gMaxRefresh;
1044
1045			monitor_info info;
1046			if (fScreenMode.GetMonitorInfo(info) == B_OK) {
1047				min = max_c(info.min_vertical_frequency, min);
1048				max = min_c(info.max_vertical_frequency, max);
1049			}
1050
1051			RefreshWindow *fRefreshWindow = new RefreshWindow(
1052				fRefreshField->ConvertToScreen(B_ORIGIN), fSelected.refresh,
1053				min, max);
1054			fRefreshWindow->Show();
1055			break;
1056		}
1057
1058		case SET_CUSTOM_REFRESH_MSG:
1059		{
1060			// user pressed "done" in "Other���" refresh dialog;
1061			// select the refresh rate chosen
1062			message->FindFloat("refresh", &fSelected.refresh);
1063
1064			_UpdateRefreshControl();
1065			_CheckApplyEnabled();
1066			break;
1067		}
1068
1069		case POP_COMBINE_DISPLAYS_MSG:
1070		{
1071			// new combine mode has bee chosen
1072			int32 mode;
1073			if (message->FindInt32("mode", &mode) == B_OK)
1074				fSelected.combine = (combine_mode)mode;
1075
1076			_CheckResolutionMenu();
1077			_CheckApplyEnabled();
1078			break;
1079		}
1080
1081		case POP_SWAP_DISPLAYS_MSG:
1082			message->FindBool("swap", &fSelected.swap_displays);
1083			_CheckApplyEnabled();
1084			break;
1085
1086		case POP_USE_LAPTOP_PANEL_MSG:
1087			message->FindBool("use", &fSelected.use_laptop_panel);
1088			_CheckApplyEnabled();
1089			break;
1090
1091		case POP_TV_STANDARD_MSG:
1092			message->FindInt32("tv_standard", (int32 *)&fSelected.tv_standard);
1093			_CheckApplyEnabled();
1094			break;
1095
1096		case BUTTON_LAUNCH_BACKGROUNDS_MSG:
1097			if (be_roster->Launch(kBackgroundsSignature) == B_ALREADY_RUNNING) {
1098				app_info info;
1099				be_roster->GetAppInfo(kBackgroundsSignature, &info);
1100				be_roster->ActivateApp(info.team);
1101			}
1102			break;
1103
1104		case BUTTON_DEFAULTS_MSG:
1105		{
1106			// TODO: get preferred settings of screen
1107			fSelected.width = 640;
1108			fSelected.height = 480;
1109			fSelected.space = B_CMAP8;
1110			fSelected.refresh = 60.0;
1111			fSelected.combine = kCombineDisable;
1112			fSelected.swap_displays = false;
1113			fSelected.use_laptop_panel = false;
1114			fSelected.tv_standard = 0;
1115
1116			// TODO: workspace defaults
1117
1118			_UpdateControls();
1119			break;
1120		}
1121
1122		case BUTTON_UNDO_MSG:
1123			fUndoScreenMode.Revert();
1124			_UpdateActiveMode();
1125			break;
1126
1127		case BUTTON_REVERT_MSG:
1128		{
1129			fModified = false;
1130			fBootWorkspaceApplied = false;
1131
1132			// ScreenMode::Revert() assumes that we first set the correct
1133			// number of workspaces
1134
1135			BPrivate::set_workspaces_layout(fOriginalWorkspacesColumns,
1136				fOriginalWorkspacesRows);
1137			_UpdateWorkspaceButtons();
1138
1139			fScreenMode.Revert();
1140			_UpdateActiveMode();
1141
1142			BScreen screen(this);
1143			screen.SetBrightness(fOriginalBrightness);
1144			fBrightnessSlider->SetValue(fOriginalBrightness * 255);
1145			break;
1146		}
1147
1148		case BUTTON_APPLY_MSG:
1149			_Apply();
1150			break;
1151
1152		case MAKE_INITIAL_MSG:
1153			// user pressed "keep" in confirmation dialog
1154			fModified = true;
1155			_UpdateActiveMode();
1156			break;
1157
1158		case UPDATE_DESKTOP_COLOR_MSG:
1159			PostMessage(message, fMonitorView);
1160			break;
1161
1162		case SLIDER_BRIGHTNESS_MSG:
1163		{
1164			BScreen screen(this);
1165			screen.SetBrightness(message->FindInt32("be:value") / 255.f);
1166			_CheckApplyEnabled();
1167			break;
1168		}
1169
1170		default:
1171			BWindow::MessageReceived(message);
1172	}
1173}
1174
1175
1176status_t
1177ScreenWindow::_WriteVesaModeFile(const screen_mode& mode) const
1178{
1179	BPath path;
1180	status_t status = find_directory(B_USER_SETTINGS_DIRECTORY, &path, true);
1181	if (status < B_OK)
1182		return status;
1183
1184	path.Append("kernel/drivers");
1185	status = create_directory(path.Path(), 0755);
1186	if (status < B_OK)
1187		return status;
1188
1189	path.Append("vesa");
1190	BFile file;
1191	status = file.SetTo(path.Path(), B_CREATE_FILE | B_WRITE_ONLY | B_ERASE_FILE);
1192	if (status < B_OK)
1193		return status;
1194
1195	char buffer[256];
1196	snprintf(buffer, sizeof(buffer), "mode %" B_PRId32 " %" B_PRId32 " %"
1197		B_PRId32 "\n", mode.width, mode.height, mode.BitsPerPixel());
1198
1199	ssize_t bytesWritten = file.Write(buffer, strlen(buffer));
1200	if (bytesWritten < B_OK)
1201		return bytesWritten;
1202
1203	return B_OK;
1204}
1205
1206
1207void
1208ScreenWindow::_BuildSupportedColorSpaces()
1209{
1210	fSupportedColorSpaces = 0;
1211
1212	for (int32 i = 0; i < kColorSpaceCount; i++) {
1213		for (int32 j = 0; j < fScreenMode.CountModes(); j++) {
1214			if (fScreenMode.ModeAt(j).space == kColorSpaces[i].space) {
1215				fSupportedColorSpaces |= 1 << i;
1216				break;
1217			}
1218		}
1219	}
1220}
1221
1222
1223void
1224ScreenWindow::_CheckApplyEnabled()
1225{
1226	bool applyEnabled = true;
1227
1228	if (fSelected == fActive) {
1229		applyEnabled = false;
1230		if (fAllWorkspacesItem->IsMarked()) {
1231			screen_mode screenMode;
1232			const int32 workspaceCount = count_workspaces();
1233			for (int32 i = 0; i < workspaceCount; i++) {
1234				fScreenMode.Get(screenMode, i);
1235				if (screenMode != fSelected) {
1236					applyEnabled = true;
1237					break;
1238				}
1239			}
1240		}
1241	}
1242
1243	fApplyButton->SetEnabled(applyEnabled);
1244
1245	uint32 columns;
1246	uint32 rows;
1247	BPrivate::get_workspaces_layout(&columns, &rows);
1248
1249	BScreen screen(this);
1250	float brightness = -1;
1251	screen.GetBrightness(&brightness);
1252
1253	fRevertButton->SetEnabled(columns != fOriginalWorkspacesColumns
1254		|| rows != fOriginalWorkspacesRows
1255		|| brightness != fOriginalBrightness
1256		|| fSelected != fOriginal);
1257}
1258
1259
1260void
1261ScreenWindow::_UpdateOriginal()
1262{
1263	BPrivate::get_workspaces_layout(&fOriginalWorkspacesColumns,
1264		&fOriginalWorkspacesRows);
1265
1266	fScreenMode.Get(fOriginal);
1267	fScreenMode.UpdateOriginalModes();
1268}
1269
1270
1271void
1272ScreenWindow::_UpdateMonitor()
1273{
1274	monitor_info info;
1275	float diagonalInches;
1276	status_t status = fScreenMode.GetMonitorInfo(info, &diagonalInches);
1277	if (status == B_OK) {
1278		char text[512];
1279		snprintf(text, sizeof(text), "%s%s%s %g\"", info.vendor,
1280			info.name[0] ? " " : "", info.name, diagonalInches);
1281
1282		fMonitorInfo->SetText(text);
1283
1284		if (fMonitorInfo->IsHidden(fMonitorInfo))
1285			fMonitorInfo->Show();
1286	} else {
1287		if (!fMonitorInfo->IsHidden(fMonitorInfo))
1288			fMonitorInfo->Hide();
1289	}
1290
1291	// Add info about the graphics device
1292
1293	accelerant_device_info deviceInfo;
1294
1295	if (fScreenMode.GetDeviceInfo(deviceInfo) == B_OK) {
1296		BString deviceString;
1297
1298		if (deviceInfo.name[0] && deviceInfo.chipset[0]) {
1299			deviceString.SetToFormat("%s (%s)", deviceInfo.name,
1300				deviceInfo.chipset);
1301		} else if (deviceInfo.name[0] || deviceInfo.chipset[0]) {
1302			deviceString
1303				= deviceInfo.name[0] ? deviceInfo.name : deviceInfo.chipset;
1304		}
1305
1306		fDeviceInfo->SetText(deviceString);
1307	}
1308
1309
1310	char text[512];
1311	size_t length = 0;
1312	text[0] = 0;
1313
1314	if (status == B_OK) {
1315		if (info.min_horizontal_frequency != 0
1316			&& info.min_vertical_frequency != 0
1317			&& info.max_pixel_clock != 0) {
1318			length = snprintf(text, sizeof(text),
1319				B_TRANSLATE("Horizonal frequency:\t%lu - %lu kHz\n"
1320				"Vertical frequency:\t%lu - %lu Hz\n\n"
1321				"Maximum pixel clock:\t%g MHz"),
1322				info.min_horizontal_frequency, info.max_horizontal_frequency,
1323				info.min_vertical_frequency, info.max_vertical_frequency,
1324				info.max_pixel_clock / 1000.0);
1325		}
1326		if (info.serial_number[0] && length < sizeof(text)) {
1327			length += snprintf(text + length, sizeof(text) - length,
1328				B_TRANSLATE("%sSerial no.: %s"), length ? "\n\n" : "",
1329				info.serial_number);
1330			if (info.produced.week != 0 && info.produced.year != 0
1331				&& length < sizeof(text)) {
1332				length += snprintf(text + length, sizeof(text) - length,
1333					" (%u/%u)", info.produced.week, info.produced.year);
1334			}
1335		}
1336	}
1337
1338	if (text[0])
1339		fMonitorView->SetToolTip(text);
1340}
1341
1342
1343void
1344ScreenWindow::_UpdateColorLabel()
1345{
1346	BString string;
1347	string << fSelected.BitsPerPixel() << " " << B_TRANSLATE("bits/pixel");
1348	fColorsMenu->Superitem()->SetLabel(string.String());
1349}
1350
1351
1352void
1353ScreenWindow::_Apply()
1354{
1355	// make checkpoint, so we can undo these changes
1356	fUndoScreenMode.UpdateOriginalModes();
1357
1358	status_t status = fScreenMode.Set(fSelected);
1359	if (status == B_OK) {
1360		// use the mode that has eventually been set and
1361		// thus we know to be working; it can differ from
1362		// the mode selected by user due to hardware limitation
1363		display_mode newMode;
1364		BScreen screen(this);
1365		screen.GetMode(&newMode);
1366
1367		if (fAllWorkspacesItem->IsMarked()) {
1368			int32 originatingWorkspace = current_workspace();
1369			const int32 workspaceCount = count_workspaces();
1370			for (int32 i = 0; i < workspaceCount; i++) {
1371				if (i != originatingWorkspace)
1372					screen.SetMode(i, &newMode, true);
1373			}
1374			fBootWorkspaceApplied = true;
1375		} else {
1376			if (current_workspace() == 0)
1377				fBootWorkspaceApplied = true;
1378		}
1379
1380		fActive = fSelected;
1381
1382		// TODO: only show alert when this is an unknown mode
1383		BAlert* window = new AlertWindow(this);
1384		window->Go(NULL);
1385	} else {
1386		char message[256];
1387		snprintf(message, sizeof(message),
1388			B_TRANSLATE("The screen mode could not be set:\n\t%s\n"),
1389			screen_errors(status));
1390		BAlert* alert = new BAlert(B_TRANSLATE("Warning"), message,
1391			B_TRANSLATE("OK"), NULL, NULL,
1392			B_WIDTH_AS_USUAL, B_WARNING_ALERT);
1393		alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
1394		alert->Go();
1395	}
1396}
1397