1/*
2 * Copyright 2001-2013, 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	fHWInterface(interface),
46	fDriver(interface != NULL ? interface->CreateDrawingEngine() : NULL)
47{
48}
49
50
51Screen::Screen()
52	:
53	fID(-1)
54{
55}
56
57
58Screen::~Screen()
59{
60	Shutdown();
61}
62
63
64/*! Finds the mode in the mode list that is closest to the mode specified.
65	As long as the mode list is not empty, this method will always succeed.
66*/
67status_t
68Screen::Initialize()
69{
70	status_t status = B_NO_INIT;
71
72	if (fHWInterface.IsSet()) {
73		// init the graphics hardware
74		status = fHWInterface->Initialize();
75	}
76
77	return status;
78}
79
80
81void
82Screen::Shutdown()
83{
84	if (fHWInterface.IsSet())
85		fHWInterface->Shutdown();
86}
87
88
89status_t
90Screen::SetMode(const display_mode& mode)
91{
92	display_mode current;
93	GetMode(current);
94	if (!memcmp(&mode, &current, sizeof(display_mode)))
95		return B_OK;
96
97	gBitmapManager->SuspendOverlays();
98
99	status_t status = fHWInterface->SetMode(mode);
100		// any attached DrawingEngines will be notified
101
102	gBitmapManager->ResumeOverlays();
103
104	return status;
105}
106
107
108status_t
109Screen::SetMode(uint16 width, uint16 height, uint32 colorSpace,
110	const display_timing& timing)
111{
112	display_mode mode;
113	mode.timing = timing;
114	mode.space = colorSpace;
115	mode.virtual_width = width;
116	mode.virtual_height = height;
117	mode.h_display_start = 0;
118	mode.v_display_start = 0;
119	mode.flags = 0;
120
121	return SetMode(mode);
122}
123
124
125status_t
126Screen::SetBestMode(uint16 width, uint16 height, uint32 colorSpace,
127	float frequency, bool strict)
128{
129	// search for a matching mode
130	display_mode* modes = NULL;
131	uint32 count;
132	status_t status = fHWInterface->GetModeList(&modes, &count);
133	if (status < B_OK)
134		return status;
135	if (count <= 0)
136		return B_ERROR;
137
138	int32 index = _FindBestMode(modes, count, width, height, colorSpace,
139		frequency);
140	if (index < 0) {
141		debug_printf("app_server: Finding best mode for %ux%u (%" B_PRIu32
142			", %g Hz%s) failed\n", width, height, colorSpace, frequency,
143			strict ? ", strict" : "");
144
145		if (strict) {
146			delete[] modes;
147			return B_ERROR;
148		} else {
149			index = 0;
150			// Just use the first mode in the list
151			debug_printf("app_server: Use %ux%u (%" B_PRIu32 ") instead.\n",
152				modes[0].timing.h_total, modes[0].timing.v_total, modes[0].space);
153		}
154	}
155
156	display_mode mode = modes[index];
157	delete[] modes;
158
159	float modeFrequency = get_mode_frequency(mode);
160	display_mode originalMode = mode;
161	bool adjusted = false;
162
163	if (modeFrequency != frequency) {
164		// adjust timing to fit the requested frequency if needed
165		// (taken from Screen preferences application)
166		mode.timing.pixel_clock = ((uint32)mode.timing.h_total
167			* mode.timing.v_total / 10 * int32(frequency * 10)) / 1000;
168		adjusted = true;
169	}
170	status = SetMode(mode);
171	if (status != B_OK && adjusted) {
172		// try again with the unchanged mode
173		status = SetMode(originalMode);
174	}
175
176	return status;
177}
178
179
180status_t
181Screen::SetPreferredMode()
182{
183	display_mode mode;
184	status_t status = fHWInterface->GetPreferredMode(&mode);
185	if (status != B_OK)
186		return status;
187
188	return SetMode(mode);
189}
190
191
192void
193Screen::GetMode(display_mode& mode) const
194{
195	fHWInterface->GetMode(&mode);
196}
197
198
199void
200Screen::GetMode(uint16 &width, uint16 &height, uint32 &colorspace,
201	float &frequency) const
202{
203	display_mode mode;
204	fHWInterface->GetMode(&mode);
205
206	width = mode.virtual_width;
207	height = mode.virtual_height;
208	colorspace = mode.space;
209	frequency = get_mode_frequency(mode);
210}
211
212
213status_t
214Screen::GetMonitorInfo(monitor_info& info) const
215{
216	return fHWInterface->GetMonitorInfo(&info);
217}
218
219
220void
221Screen::SetFrame(const BRect& rect)
222{
223	// TODO: multi-monitor support...
224}
225
226
227BRect
228Screen::Frame() const
229{
230	display_mode mode;
231	fHWInterface->GetMode(&mode);
232
233	return BRect(0, 0, mode.virtual_width - 1, mode.virtual_height - 1);
234}
235
236
237color_space
238Screen::ColorSpace() const
239{
240	display_mode mode;
241	fHWInterface->GetMode(&mode);
242
243	return (color_space)mode.space;
244}
245
246
247/*!	\brief Returns the mode that matches the given criteria best.
248	The "width" argument is the only hard argument, the rest will be adapted
249	as needed.
250*/
251int32
252Screen::_FindBestMode(const display_mode* modes, uint32 count,
253	uint16 width, uint16 height, uint32 colorSpace, float frequency) const
254{
255	int32 bestDiff = 0;
256	int32 bestIndex = -1;
257	for (uint32 i = 0; i < count; i++) {
258		const display_mode& mode = modes[i];
259		if (mode.virtual_width != width)
260			continue;
261
262		// compute some random equality score
263		// TODO: check if these scores make sense
264		int32 diff = 1000 * abs(mode.timing.v_display - height)
265			+ int32(fabs(get_mode_frequency(mode) - frequency) * 10)
266			+ 100 * abs((int)(mode.space - colorSpace));
267
268		if (bestIndex == -1 || diff < bestDiff) {
269			bestDiff = diff;
270			bestIndex = i;
271		}
272	}
273
274	return bestIndex;
275}
276