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