xref: /haiku/src/servers/app/Screen.cpp (revision 4f2fd49bdc6078128b1391191e4edac647044c3d)
1 /*
2  * Copyright (c) 2001-2008, 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 	: fID(id),
44 	  fDriver(interface ? new DrawingEngine(interface) : NULL),
45 	  fHWInterface(interface),
46 	  fIsDefault(true)
47 {
48 }
49 
50 
51 Screen::Screen()
52 	: fID(-1),
53 	  fDriver(NULL),
54 	  fHWInterface(NULL),
55 	  fIsDefault(true)
56 {
57 }
58 
59 
60 Screen::~Screen()
61 {
62 	Shutdown();
63 	delete fDriver;
64 	delete fHWInterface;
65 }
66 
67 /*! Finds the mode in the mode list that is closest to the mode specified.
68 	As long as the mode list is not empty, this method will always succeed.
69 */
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, bool makeDefault)
93 {
94 	display_mode current;
95 	GetMode(&current);
96 	// TODO: decide how best to handle the flags here - things like
97 	// the screen preflet will generally always set the flags to 0,
98 	// while it seems asking the accelerant to automatically pick the
99 	// best mode might not necessarily. For the time being, match the
100 	// flags before doing the mode comparison in order to prevent
101 	// mode switches for otherwise identical modes (this was relatively
102 	// easily observed on at least the radeon accelerant - on first boot
103 	// the best mode picked included a flag mask of 0xffffffff ;
104 	// if you switched the resolution of one workspace to something else
105 	// and then back to the resolution it started with, you would observe
106 	// a mode switch when jumping between that workspace and the others
107 	// that were still using the automatically set default mode)
108 	current.flags = mode.flags;
109 	if (!memcmp(&mode, &current, sizeof(display_mode)))
110 		return B_OK;
111 
112 	gBitmapManager->SuspendOverlays();
113 
114 	status_t status = fHWInterface->SetMode(mode);
115 		// any attached DrawingEngines will be notified
116 
117 	gBitmapManager->ResumeOverlays();
118 
119 	if (status >= B_OK)
120 		fIsDefault = makeDefault;
121 
122 	return status;
123 }
124 
125 
126 status_t
127 Screen::SetMode(uint16 width, uint16 height, uint32 colorSpace,
128 	const display_timing& timing, bool makeDefault)
129 {
130 	display_mode mode;
131 	mode.timing = timing;
132 	mode.space = colorSpace;
133 	mode.virtual_width = width;
134 	mode.virtual_height = height;
135 	mode.h_display_start = 0;
136 	mode.v_display_start = 0;
137 	mode.flags = 0;
138 
139 	return SetMode(mode, makeDefault);
140 }
141 
142 
143 status_t
144 Screen::SetBestMode(uint16 width, uint16 height, uint32 colorSpace,
145 	float frequency, bool strict)
146 {
147 	// search for a matching mode
148 	display_mode* modes = NULL;
149 	uint32 count;
150 	status_t status = fHWInterface->GetModeList(&modes, &count);
151 	if (status < B_OK)
152 		return status;
153 	if (count <= 0)
154 		return B_ERROR;
155 
156 	int32 index = _FindBestMode(modes, count, width, height, colorSpace,
157 		frequency);
158 	if (index < 0) {
159 		if (strict) {
160 			debug_printf("Finding best mode failed");
161 			delete[] modes;
162 			return B_ERROR;
163 		} else {
164 			index = 0;
165 			// Just use the first mode in the list
166 		}
167 	}
168 
169 	display_mode mode = modes[index];
170 	delete[] modes;
171 
172 	float modeFrequency = get_mode_frequency(mode);
173 	display_mode originalMode = mode;
174 	bool adjusted = false;
175 
176 	if (modeFrequency != frequency) {
177 		// adjust timing to fit the requested frequency if needed
178 		// (taken from Screen preferences application)
179 		mode.timing.pixel_clock = ((uint32)mode.timing.h_total
180 			* mode.timing.v_total / 10 * int32(frequency * 10)) / 1000;
181 		adjusted = true;
182 	}
183 	status = SetMode(mode, false);
184 	if (status < B_OK && adjusted) {
185 		// try again with the unchanged mode
186 		status = SetMode(originalMode, false);
187 	}
188 
189 	return status;
190 }
191 
192 
193 status_t
194 Screen::SetPreferredMode()
195 {
196 	display_mode mode;
197 	status_t status = fHWInterface->GetPreferredMode(&mode);
198 	if (status < B_OK)
199 		return status;
200 
201 	return SetMode(mode, false);
202 }
203 
204 
205 void
206 Screen::GetMode(display_mode* mode) const
207 {
208 	fHWInterface->GetMode(mode);
209 }
210 
211 
212 void
213 Screen::GetMode(uint16 &width, uint16 &height, uint32 &colorspace,
214 	float &frequency) const
215 {
216 	display_mode mode;
217 	fHWInterface->GetMode(&mode);
218 
219 	width = mode.virtual_width;
220 	height = mode.virtual_height;
221 	colorspace = mode.space;
222 	frequency = get_mode_frequency(mode);
223 }
224 
225 
226 status_t
227 Screen::GetMonitorInfo(monitor_info& info) const
228 {
229 	return fHWInterface->GetMonitorInfo(&info);
230 }
231 
232 
233 BRect
234 Screen::Frame() const
235 {
236 	display_mode mode;
237 	fHWInterface->GetMode(&mode);
238 
239 	return BRect(0, 0, mode.virtual_width - 1, mode.virtual_height - 1);
240 }
241 
242 
243 color_space
244 Screen::ColorSpace() const
245 {
246 	display_mode mode;
247 	fHWInterface->GetMode(&mode);
248 
249 	return (color_space)mode.space;
250 }
251 
252 
253 /*!	\brief Returns the mode that matches the given criteria best.
254 	The "width" argument is the only hard argument, the rest will be adapted
255 	as needed.
256 */
257 int32
258 Screen::_FindBestMode(const display_mode* modes, uint32 count,
259 	uint16 width, uint16 height, uint32 colorSpace, float frequency) const
260 {
261 	int32 bestDiff = 0;
262 	int32 bestIndex = -1;
263 	for (uint32 i = 0; i < count; i++) {
264 		const display_mode& mode = modes[i];
265 		if (mode.virtual_width != width)
266 			continue;
267 
268 		// compute some random equality score
269 		// TODO: check if these scores make sense
270 		int32 diff = 1000 * abs(mode.timing.v_display - height)
271 			+ int32(fabs(get_mode_frequency(mode) - frequency) * 10)
272 			+ 100 * abs(mode.space - colorSpace);
273 
274 		if (bestIndex == -1 || diff < bestDiff) {
275 			bestDiff = diff;
276 			bestIndex = i;
277 		}
278 	}
279 
280 	return bestIndex;
281 }
282