xref: /haiku/src/servers/app/Screen.cpp (revision a5a3b2d9a3d95cbae71eaf371708c73a1780ac0d)
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 	fDriver(NULL),
55 	fHWInterface(NULL)
56 {
57 }
58 
59 
60 Screen::~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 */
71 status_t
72 Screen::Initialize()
73 {
74 	if (fHWInterface) {
75 		// init the graphics hardware
76 		return fHWInterface->Initialize();
77 	}
78 
79 	return B_NO_INIT;
80 }
81 
82 
83 void
84 Screen::Shutdown()
85 {
86 	if (fHWInterface)
87 		fHWInterface->Shutdown();
88 }
89 
90 
91 status_t
92 Screen::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 
110 status_t
111 Screen::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 
127 status_t
128 Screen::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 		debug_printf("app_server: Finding best mode for %ux%u (%" B_PRIu32
144 			", %g Hz%s) failed\n", width, height, colorSpace, frequency,
145 			strict ? ", strict" : "");
146 
147 		if (strict) {
148 			delete[] modes;
149 			return B_ERROR;
150 		} else {
151 			index = 0;
152 			// Just use the first mode in the list
153 			debug_printf("app_server: Use %ux%u (%" B_PRIu32 ") instead.\n",
154 				modes[0].timing.h_total, modes[0].timing.v_total, modes[0].space);
155 		}
156 	}
157 
158 	display_mode mode = modes[index];
159 	delete[] modes;
160 
161 	float modeFrequency = get_mode_frequency(mode);
162 	display_mode originalMode = mode;
163 	bool adjusted = false;
164 
165 	if (modeFrequency != frequency) {
166 		// adjust timing to fit the requested frequency if needed
167 		// (taken from Screen preferences application)
168 		mode.timing.pixel_clock = ((uint32)mode.timing.h_total
169 			* mode.timing.v_total / 10 * int32(frequency * 10)) / 1000;
170 		adjusted = true;
171 	}
172 	status = SetMode(mode);
173 	if (status != B_OK && adjusted) {
174 		// try again with the unchanged mode
175 		status = SetMode(originalMode);
176 	}
177 
178 	return status;
179 }
180 
181 
182 status_t
183 Screen::SetPreferredMode()
184 {
185 	display_mode mode;
186 	status_t status = fHWInterface->GetPreferredMode(&mode);
187 	if (status != B_OK)
188 		return status;
189 
190 	return SetMode(mode);
191 }
192 
193 
194 void
195 Screen::GetMode(display_mode& mode) const
196 {
197 	fHWInterface->GetMode(&mode);
198 }
199 
200 
201 void
202 Screen::GetMode(uint16 &width, uint16 &height, uint32 &colorspace,
203 	float &frequency) const
204 {
205 	display_mode mode;
206 	fHWInterface->GetMode(&mode);
207 
208 	width = mode.virtual_width;
209 	height = mode.virtual_height;
210 	colorspace = mode.space;
211 	frequency = get_mode_frequency(mode);
212 }
213 
214 
215 status_t
216 Screen::GetMonitorInfo(monitor_info& info) const
217 {
218 	return fHWInterface->GetMonitorInfo(&info);
219 }
220 
221 
222 void
223 Screen::SetFrame(const BRect& rect)
224 {
225 	// TODO: multi-monitor support...
226 }
227 
228 
229 BRect
230 Screen::Frame() const
231 {
232 	display_mode mode;
233 	fHWInterface->GetMode(&mode);
234 
235 	return BRect(0, 0, mode.virtual_width - 1, mode.virtual_height - 1);
236 }
237 
238 
239 color_space
240 Screen::ColorSpace() const
241 {
242 	display_mode mode;
243 	fHWInterface->GetMode(&mode);
244 
245 	return (color_space)mode.space;
246 }
247 
248 
249 /*!	\brief Returns the mode that matches the given criteria best.
250 	The "width" argument is the only hard argument, the rest will be adapted
251 	as needed.
252 */
253 int32
254 Screen::_FindBestMode(const display_mode* modes, uint32 count,
255 	uint16 width, uint16 height, uint32 colorSpace, float frequency) const
256 {
257 	int32 bestDiff = 0;
258 	int32 bestIndex = -1;
259 	for (uint32 i = 0; i < count; i++) {
260 		const display_mode& mode = modes[i];
261 		if (mode.virtual_width != width)
262 			continue;
263 
264 		// compute some random equality score
265 		// TODO: check if these scores make sense
266 		int32 diff = 1000 * abs(mode.timing.v_display - height)
267 			+ int32(fabs(get_mode_frequency(mode) - frequency) * 10)
268 			+ 100 * abs(mode.space - colorSpace);
269 
270 		if (bestIndex == -1 || diff < bestDiff) {
271 			bestDiff = diff;
272 			bestIndex = i;
273 		}
274 	}
275 
276 	return bestIndex;
277 }
278