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