1/*
2 * Copyright 2005-2009, Haiku.
3 * Distributed under the terms of the MIT License.
4 *
5 * Authors:
6 *		Axel D��rfler, axeld@pinc-software.de
7 */
8
9
10#include "VirtualScreen.h"
11
12#include "HWInterface.h"
13#include "Desktop.h"
14
15#include <new>
16
17
18VirtualScreen::VirtualScreen()
19	:
20	fScreenList(4, true),
21	fDrawingEngine(NULL),
22	fHWInterface(NULL)
23{
24}
25
26
27VirtualScreen::~VirtualScreen()
28{
29	_Reset();
30}
31
32
33void
34VirtualScreen::_Reset()
35{
36	ScreenList list;
37	for (int32 i = 0; i < fScreenList.CountItems(); i++) {
38		screen_item* item = fScreenList.ItemAt(i);
39
40		list.AddItem(item->screen);
41	}
42
43	gScreenManager->ReleaseScreens(list);
44	fScreenList.MakeEmpty();
45
46	fFrame.Set(0, 0, 0, 0);
47	fDrawingEngine = NULL;
48	fHWInterface = NULL;
49}
50
51
52status_t
53VirtualScreen::SetConfiguration(Desktop& desktop,
54	ScreenConfigurations& configurations, uint32* _changedScreens)
55{
56	// Remember previous screen modes
57
58	typedef std::map<Screen*, display_mode> ScreenModeMap;
59	ScreenModeMap previousModes;
60	bool previousModesFailed = false;
61
62	if (_changedScreens != NULL) {
63		*_changedScreens = 0;
64
65		try {
66			for (int32 i = 0; i < fScreenList.CountItems(); i++) {
67				Screen* screen = fScreenList.ItemAt(i)->screen;
68
69				display_mode mode;
70				screen->GetMode(mode);
71
72				previousModes.insert(std::make_pair(screen, mode));
73			}
74		} catch (...) {
75			previousModesFailed = true;
76			*_changedScreens = ~0L;
77		}
78	}
79
80	_Reset();
81
82	ScreenList list;
83	status_t status = gScreenManager->AcquireScreens(&desktop, NULL, 0,
84		desktop.TargetScreen(), false, list);
85	if (status != B_OK) {
86		// TODO: we would try again here with force == true
87		return status;
88	}
89
90	for (int32 i = 0; i < list.CountItems(); i++) {
91		Screen* screen = list.ItemAt(i);
92
93		AddScreen(screen, configurations);
94
95		if (!previousModesFailed && _changedScreens != NULL) {
96			// Figure out which screens have changed their mode
97			display_mode mode;
98			screen->GetMode(mode);
99
100			ScreenModeMap::const_iterator found = previousModes.find(screen);
101			if (found != previousModes.end()
102				&& memcmp(&mode, &found->second, sizeof(display_mode)))
103				*_changedScreens |= 1 << i;
104		}
105	}
106
107	UpdateFrame();
108	return B_OK;
109}
110
111
112status_t
113VirtualScreen::AddScreen(Screen* screen, ScreenConfigurations& configurations)
114{
115	screen_item* item = new(std::nothrow) screen_item;
116	if (item == NULL)
117		return B_NO_MEMORY;
118
119	item->screen = screen;
120
121	status_t status = B_ERROR;
122	display_mode mode;
123	if (_GetMode(screen, configurations, mode) == B_OK) {
124		// we found settings for this screen, and try to apply them now
125		status = screen->SetMode(mode);
126	}
127	if (status != B_OK) {
128		status_t status = screen->SetPreferredMode();
129		if (status != B_OK)
130			status = screen->SetBestMode(1024, 768, B_RGB32, 60.f);
131		if (status != B_OK)
132			screen->SetBestMode(800, 600, B_RGB32, 60.f, false);
133	}
134
135	// TODO: this works only for single screen configurations
136	fDrawingEngine = screen->GetDrawingEngine();
137	fHWInterface = screen->HWInterface();
138	fFrame = screen->Frame();
139	item->frame = fFrame;
140
141	fScreenList.AddItem(item);
142
143	return B_OK;
144}
145
146
147status_t
148VirtualScreen::RemoveScreen(Screen* screen)
149{
150	// not implemented yet (config changes when running)
151	return B_ERROR;
152}
153
154
155void
156VirtualScreen::UpdateFrame()
157{
158	int32 virtualWidth = 0, virtualHeight = 0;
159
160	for (int32 i = 0; i < fScreenList.CountItems(); i++) {
161		Screen* screen = fScreenList.ItemAt(i)->screen;
162
163		uint16 width, height;
164		uint32 colorSpace;
165		float frequency;
166		screen->GetMode(width, height, colorSpace, frequency);
167
168		// TODO: compute virtual size depending on the actual screen position!
169		virtualWidth += width;
170		virtualHeight = max_c(virtualHeight, height);
171	}
172
173	fFrame.Set(0, 0, virtualWidth - 1, virtualHeight - 1);
174}
175
176
177/*!	Returns the smallest frame that spans over all screens
178*/
179BRect
180VirtualScreen::Frame() const
181{
182	return fFrame;
183}
184
185
186Screen*
187VirtualScreen::ScreenAt(int32 index) const
188{
189	screen_item* item = fScreenList.ItemAt(index);
190	if (item != NULL)
191		return item->screen;
192
193	return NULL;
194}
195
196
197Screen*
198VirtualScreen::ScreenByID(int32 id) const
199{
200	for (int32 i = fScreenList.CountItems(); i-- > 0;) {
201		screen_item* item = fScreenList.ItemAt(i);
202
203		if (item->screen->ID() == id || id == B_MAIN_SCREEN_ID.id)
204			return item->screen;
205	}
206
207	return NULL;
208}
209
210
211BRect
212VirtualScreen::ScreenFrameAt(int32 index) const
213{
214	screen_item* item = fScreenList.ItemAt(index);
215	if (item != NULL)
216		return item->frame;
217
218	return BRect(0, 0, 0, 0);
219}
220
221
222int32
223VirtualScreen::CountScreens() const
224{
225	return fScreenList.CountItems();
226}
227
228
229status_t
230VirtualScreen::_GetMode(Screen* screen, ScreenConfigurations& configurations,
231	display_mode& mode) const
232{
233	monitor_info info;
234	bool hasInfo = screen->GetMonitorInfo(info) == B_OK;
235
236	screen_configuration* configuration = configurations.BestFit(screen->ID(),
237		hasInfo ? &info : NULL);
238	if (configuration == NULL)
239		return B_NAME_NOT_FOUND;
240
241	mode = configuration->mode;
242	configuration->is_current = true;
243
244	return B_OK;
245}
246
247