1/*
2 * Copyright 2005-2013, 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
128	if (status != B_OK) {
129		// We found no configuration or it wasn't valid, try to fallback to
130		// sane values
131		status = screen->SetPreferredMode();
132		if (status == B_OK) {
133			monitor_info info;
134			bool hasInfo = screen->GetMonitorInfo(info) == B_OK;
135			screen->GetMode(mode);
136			configurations.Set(screen->ID(), hasInfo ? &info : NULL, screen->Frame(), mode);
137		}
138		if (status != B_OK)
139			status = screen->SetBestMode(1024, 768, B_RGB32, 60.f);
140		if (status != B_OK)
141			status = screen->SetBestMode(800, 600, B_RGB32, 60.f, false);
142		if (status != B_OK) {
143			debug_printf("app_server: Failed to set mode: %s\n",
144				strerror(status));
145
146			delete item;
147			return status;
148		}
149	}
150
151	// TODO: this works only for single screen configurations
152	fDrawingEngine = screen->GetDrawingEngine();
153	fHWInterface = screen->HWInterface();
154	fFrame = screen->Frame();
155	item->frame = fFrame;
156
157	fScreenList.AddItem(item);
158
159	return B_OK;
160}
161
162
163status_t
164VirtualScreen::RemoveScreen(Screen* screen)
165{
166	// not implemented yet (config changes when running)
167	return B_ERROR;
168}
169
170
171void
172VirtualScreen::UpdateFrame()
173{
174	int32 virtualWidth = 0, virtualHeight = 0;
175
176	for (int32 i = 0; i < fScreenList.CountItems(); i++) {
177		Screen* screen = fScreenList.ItemAt(i)->screen;
178
179		uint16 width, height;
180		uint32 colorSpace;
181		float frequency;
182		screen->GetMode(width, height, colorSpace, frequency);
183
184		// TODO: compute virtual size depending on the actual screen position!
185		virtualWidth += width;
186		virtualHeight = max_c(virtualHeight, height);
187	}
188
189	fFrame.Set(0, 0, virtualWidth - 1, virtualHeight - 1);
190}
191
192
193/*!	Returns the smallest frame that spans over all screens
194*/
195BRect
196VirtualScreen::Frame() const
197{
198	return fFrame;
199}
200
201
202Screen*
203VirtualScreen::ScreenAt(int32 index) const
204{
205	screen_item* item = fScreenList.ItemAt(index);
206	if (item != NULL)
207		return item->screen;
208
209	return NULL;
210}
211
212
213Screen*
214VirtualScreen::ScreenByID(int32 id) const
215{
216	for (int32 i = fScreenList.CountItems(); i-- > 0;) {
217		screen_item* item = fScreenList.ItemAt(i);
218
219		if (item->screen->ID() == id || id == B_MAIN_SCREEN_ID.id)
220			return item->screen;
221	}
222
223	return NULL;
224}
225
226
227BRect
228VirtualScreen::ScreenFrameAt(int32 index) const
229{
230	screen_item* item = fScreenList.ItemAt(index);
231	if (item != NULL)
232		return item->frame;
233
234	return BRect(0, 0, 0, 0);
235}
236
237
238int32
239VirtualScreen::CountScreens() const
240{
241	return fScreenList.CountItems();
242}
243
244
245status_t
246VirtualScreen::_GetMode(Screen* screen, ScreenConfigurations& configurations,
247	display_mode& mode) const
248{
249	monitor_info info;
250	bool hasInfo = screen->GetMonitorInfo(info) == B_OK;
251
252	screen_configuration* configuration = configurations.BestFit(screen->ID(),
253		hasInfo ? &info : NULL);
254	if (configuration == NULL)
255		return B_NAME_NOT_FOUND;
256
257	mode = configuration->mode;
258	configuration->is_current = true;
259
260	return B_OK;
261}
262
263