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