1/*
2 * Copyright 2001-2009, Haiku, Inc.
3 * Distributed under the terms of the MIT license.
4 *
5 * Authors:
6 *		Adi Oanca <adioanca@myrealbox.com>
7 *		Axel D��rfler, axeld@pinc-software.de
8 *		Stephan A��mus, <superstippi@gmx.de>
9 */
10
11
12#include "Screen.h"
13
14#include "BitmapManager.h"
15#include "DrawingEngine.h"
16#include "HWInterface.h"
17
18#include <Accelerant.h>
19#include <Point.h>
20#include <GraphicsDefs.h>
21
22#include <stdlib.h>
23#include <stdio.h>
24
25
26static float
27get_mode_frequency(const display_mode& mode)
28{
29	// Taken from Screen preferences
30	float timing = float(mode.timing.h_total * mode.timing.v_total);
31	if (timing == 0.0f)
32		return 0.0f;
33
34	return rint(10 * float(mode.timing.pixel_clock * 1000)
35		/ timing) / 10.0;
36}
37
38
39//	#pragma mark -
40
41
42Screen::Screen(::HWInterface *interface, int32 id)
43	:
44	fID(id),
45	fDriver(interface ? interface->CreateDrawingEngine() : NULL),
46	fHWInterface(interface)
47{
48}
49
50
51Screen::Screen()
52	:
53	fID(-1),
54	fDriver(NULL),
55	fHWInterface(NULL)
56{
57}
58
59
60Screen::~Screen()
61{
62	Shutdown();
63	delete fDriver;
64	delete fHWInterface;
65}
66
67
68/*! Finds the mode in the mode list that is closest to the mode specified.
69	As long as the mode list is not empty, this method will always succeed.
70*/
71status_t
72Screen::Initialize()
73{
74	if (fHWInterface) {
75		// init the graphics hardware
76		return fHWInterface->Initialize();
77	}
78
79	return B_NO_INIT;
80}
81
82
83void
84Screen::Shutdown()
85{
86	if (fHWInterface)
87		fHWInterface->Shutdown();
88}
89
90
91status_t
92Screen::SetMode(const display_mode& mode)
93{
94	display_mode current;
95	GetMode(current);
96	if (!memcmp(&mode, &current, sizeof(display_mode)))
97		return B_OK;
98
99	gBitmapManager->SuspendOverlays();
100
101	status_t status = fHWInterface->SetMode(mode);
102		// any attached DrawingEngines will be notified
103
104	gBitmapManager->ResumeOverlays();
105
106	return status;
107}
108
109
110status_t
111Screen::SetMode(uint16 width, uint16 height, uint32 colorSpace,
112	const display_timing& timing)
113{
114	display_mode mode;
115	mode.timing = timing;
116	mode.space = colorSpace;
117	mode.virtual_width = width;
118	mode.virtual_height = height;
119	mode.h_display_start = 0;
120	mode.v_display_start = 0;
121	mode.flags = 0;
122
123	return SetMode(mode);
124}
125
126
127status_t
128Screen::SetBestMode(uint16 width, uint16 height, uint32 colorSpace,
129	float frequency, bool strict)
130{
131	// search for a matching mode
132	display_mode* modes = NULL;
133	uint32 count;
134	status_t status = fHWInterface->GetModeList(&modes, &count);
135	if (status < B_OK)
136		return status;
137	if (count <= 0)
138		return B_ERROR;
139
140	int32 index = _FindBestMode(modes, count, width, height, colorSpace,
141		frequency);
142	if (index < 0) {
143		if (strict) {
144			debug_printf("Finding best mode failed\n");
145			delete[] modes;
146			return B_ERROR;
147		} else {
148			index = 0;
149			// Just use the first mode in the list
150		}
151	}
152
153	display_mode mode = modes[index];
154	delete[] modes;
155
156	float modeFrequency = get_mode_frequency(mode);
157	display_mode originalMode = mode;
158	bool adjusted = false;
159
160	if (modeFrequency != frequency) {
161		// adjust timing to fit the requested frequency if needed
162		// (taken from Screen preferences application)
163		mode.timing.pixel_clock = ((uint32)mode.timing.h_total
164			* mode.timing.v_total / 10 * int32(frequency * 10)) / 1000;
165		adjusted = true;
166	}
167	status = SetMode(mode);
168	if (status != B_OK && adjusted) {
169		// try again with the unchanged mode
170		status = SetMode(originalMode);
171	}
172
173	return status;
174}
175
176
177status_t
178Screen::SetPreferredMode()
179{
180	display_mode mode;
181	status_t status = fHWInterface->GetPreferredMode(&mode);
182	if (status != B_OK)
183		return status;
184
185	return SetMode(mode);
186}
187
188
189void
190Screen::GetMode(display_mode& mode) const
191{
192	fHWInterface->GetMode(&mode);
193}
194
195
196void
197Screen::GetMode(uint16 &width, uint16 &height, uint32 &colorspace,
198	float &frequency) const
199{
200	display_mode mode;
201	fHWInterface->GetMode(&mode);
202
203	width = mode.virtual_width;
204	height = mode.virtual_height;
205	colorspace = mode.space;
206	frequency = get_mode_frequency(mode);
207}
208
209
210status_t
211Screen::GetMonitorInfo(monitor_info& info) const
212{
213	return fHWInterface->GetMonitorInfo(&info);
214}
215
216
217void
218Screen::SetFrame(const BRect& rect)
219{
220	// TODO: multi-monitor support...
221}
222
223
224BRect
225Screen::Frame() const
226{
227	display_mode mode;
228	fHWInterface->GetMode(&mode);
229
230	return BRect(0, 0, mode.virtual_width - 1, mode.virtual_height - 1);
231}
232
233
234color_space
235Screen::ColorSpace() const
236{
237	display_mode mode;
238	fHWInterface->GetMode(&mode);
239
240	return (color_space)mode.space;
241}
242
243
244/*!	\brief Returns the mode that matches the given criteria best.
245	The "width" argument is the only hard argument, the rest will be adapted
246	as needed.
247*/
248int32
249Screen::_FindBestMode(const display_mode* modes, uint32 count,
250	uint16 width, uint16 height, uint32 colorSpace, float frequency) const
251{
252	int32 bestDiff = 0;
253	int32 bestIndex = -1;
254	for (uint32 i = 0; i < count; i++) {
255		const display_mode& mode = modes[i];
256		if (mode.virtual_width != width)
257			continue;
258
259		// compute some random equality score
260		// TODO: check if these scores make sense
261		int32 diff = 1000 * abs(mode.timing.v_display - height)
262			+ int32(fabs(get_mode_frequency(mode) - frequency) * 10)
263			+ 100 * abs(mode.space - colorSpace);
264
265		if (bestIndex == -1 || diff < bestDiff) {
266			bestDiff = diff;
267			bestIndex = i;
268		}
269	}
270
271	return bestIndex;
272}
273