xref: /haiku/src/servers/app/Screen.cpp (revision 2a2e7ad562841be14b2d1f8ad870780f32be2b1f)
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 	fHWInterface(interface),
46 	fDriver(interface != NULL ? interface->CreateDrawingEngine() : NULL)
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 	return status;
78 }
79 
80 
81 void
82 Screen::Shutdown()
83 {
84 	if (fHWInterface.IsSet())
85 		fHWInterface->Shutdown();
86 }
87 
88 
89 status_t
90 Screen::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 
108 status_t
109 Screen::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 
125 status_t
126 Screen::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 
180 status_t
181 Screen::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 
192 void
193 Screen::GetMode(display_mode& mode) const
194 {
195 	fHWInterface->GetMode(&mode);
196 }
197 
198 
199 void
200 Screen::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 
213 status_t
214 Screen::GetMonitorInfo(monitor_info& info) const
215 {
216 	return fHWInterface->GetMonitorInfo(&info);
217 }
218 
219 
220 void
221 Screen::SetFrame(const BRect& rect)
222 {
223 	// TODO: multi-monitor support...
224 }
225 
226 
227 BRect
228 Screen::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 
237 color_space
238 Screen::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 */
251 int32
252 Screen::_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