xref: /haiku/src/preferences/screen/ScreenMode.cpp (revision 93aeb8c3bc3f13cb1f282e3e749258a23790d947)
1 /*
2  * Copyright 2005, Haiku.
3  * Distributed under the terms of the MIT License.
4  *
5  * Authors:
6  *		Axel Dörfler, axeld@pinc-software.de
7  */
8 
9 
10 #include "ScreenMode.h"
11 
12 #include <stdlib.h>
13 #include <stdio.h>
14 
15 
16 /* Note, this headers defines a *private* interface to the Radeon accelerant.
17  * It's a solution that works with the current BeOS interface that Haiku
18  * adopted.
19  * However, it's not a nice and clean solution. Don't use this header in any
20  * application if you can avoid it. No other driver is using this, or should
21  * be using this.
22  * It will be replaced as soon as we introduce an updated accelerant interface
23  * which may even happen before R1 hits the streets.
24  */
25 
26 #include "multimon.h"	// the usual: DANGER WILL, ROBINSON!
27 
28 
29 static combine_mode
30 get_combine_mode(display_mode& mode)
31 {
32 	if (mode.flags & B_SCROLL == 0)
33 		return kCombineDisable;
34 
35 	if (mode.virtual_width == mode.timing.h_display * 2)
36 		return kCombineHorizontally;
37 
38 	if (mode.virtual_height == mode.timing.v_display * 2)
39 		return kCombineVertically;
40 
41 	return kCombineDisable;
42 }
43 
44 
45 static float
46 get_refresh_rate(display_mode& mode)
47 {
48 	// we have to be catious as refresh rate cannot be controlled directly,
49 	// so it suffers under rounding errors and hardware restrictions
50 	return rint(10 * float(mode.timing.pixel_clock * 1000) /
51 		float(mode.timing.h_total * mode.timing.v_total)) / 10.0;
52 }
53 
54 
55 /** helper to sort modes by resolution */
56 
57 static int
58 compare_mode(const void* _mode1, const void* _mode2)
59 {
60 	display_mode *mode1 = (display_mode *)_mode1;
61 	display_mode *mode2 = (display_mode *)_mode2;
62 	combine_mode combine1, combine2;
63 	uint16 width1, width2, height1, height2;
64 
65 	combine1 = get_combine_mode(*mode1);
66 	combine2 = get_combine_mode(*mode2);
67 
68 	width1 = mode1->virtual_width;
69 	height1 = mode1->virtual_height;
70 	width2 = mode2->virtual_width;
71 	height2 = mode2->virtual_height;
72 
73 	if (combine1 == kCombineHorizontally)
74 		width1 /= 2;
75 	if (combine1 == kCombineVertically)
76 		height1 /= 2;
77 	if (combine2 == kCombineHorizontally)
78 		width2 /= 2;
79 	if (combine2 == kCombineVertically)
80 		height2 /= 2;
81 
82 	if (width1 != width2)
83 		return width1 - width2;
84 
85 	if (height1 != height2)
86 		return height1 - height2;
87 
88 	return (int)(10 * get_refresh_rate(*mode1)
89 		-  10 * get_refresh_rate(*mode2));
90 }
91 
92 
93 //	#pragma mark -
94 
95 
96 int32
97 screen_mode::BitsPerPixel() const
98 {
99 	switch (space) {
100 		case B_RGB32:	return 32;
101 		case B_RGB24:	return 24;
102 		case B_RGB16:	return 16;
103 		case B_RGB15:	return 15;
104 		case B_CMAP8:	return 8;
105 		default:		return 0;
106 	}
107 }
108 
109 
110 bool
111 screen_mode::operator==(const screen_mode &other) const
112 {
113 	return !(*this != other);
114 }
115 
116 
117 bool
118 screen_mode::operator!=(const screen_mode &other) const
119 {
120 	return width != other.width || height != other.height
121 		|| space != other.space || refresh != other.refresh
122 		|| combine != other.combine
123 		|| swap_displays != other.swap_displays
124 		|| use_laptop_panel != other.use_laptop_panel
125 		|| tv_standard != other.tv_standard;
126 }
127 
128 
129 void
130 screen_mode::SetTo(display_mode& mode)
131 {
132 	width = mode.virtual_width;
133 	height = mode.virtual_height;
134 	space = (color_space)mode.space;
135 	combine = get_combine_mode(mode);
136 	refresh = get_refresh_rate(mode);
137 
138 	if (combine == kCombineHorizontally)
139 		width /= 2;
140 	else if (combine == kCombineVertically)
141 		height /= 2;
142 
143 	swap_displays = false;
144 	use_laptop_panel = false;
145 	tv_standard = 0;
146 }
147 
148 
149 //	#pragma mark -
150 
151 
152 ScreenMode::ScreenMode(BWindow* window)
153 	:
154 	fWindow(window),
155 	fUpdatedMode(false)
156 {
157 	BScreen screen(window);
158 	if (screen.GetModeList(&fModeList, &fModeCount) == B_OK) {
159 		// sort modes by resolution and refresh to make
160 		// the resolution and refresh menu look nicer
161 		qsort(fModeList, fModeCount, sizeof(display_mode), compare_mode);
162 	} else {
163 		fModeList = NULL;
164 		fModeCount = 0;
165 	}
166 }
167 
168 
169 ScreenMode::~ScreenMode()
170 {
171 	free(fModeList);
172 }
173 
174 
175 status_t
176 ScreenMode::Set(screen_mode& mode)
177 {
178 	if (!fUpdatedMode)
179 		UpdateOriginalMode();
180 
181 	BScreen screen(fWindow);
182 	SetSwapDisplays(&screen, mode.swap_displays);
183 	SetUseLaptopPanel(&screen, mode.use_laptop_panel);
184 	SetTVStandard(&screen, mode.tv_standard);
185 
186 	display_mode displayMode;
187 	if (!GetDisplayMode(mode, displayMode))
188 		return B_ENTRY_NOT_FOUND;
189 
190 	return screen.SetMode(&displayMode, true);
191 }
192 
193 
194 status_t
195 ScreenMode::Get(screen_mode& mode)
196 {
197 	display_mode displayMode;
198 	BScreen screen(fWindow);
199 	if (screen.GetMode(&displayMode) != B_OK)
200 		return B_ERROR;
201 
202 	mode.SetTo(displayMode);
203 
204 	if (GetSwapDisplays(&screen, &mode.swap_displays) != B_OK)
205 		mode.swap_displays = false;
206 	if (GetUseLaptopPanel(&screen, &mode.use_laptop_panel) != B_OK)
207 		mode.use_laptop_panel = false;
208 	if (GetTVStandard(&screen, &mode.tv_standard) != B_OK)
209 		mode.tv_standard = 0;
210 
211 	return B_OK;
212 }
213 
214 
215 status_t
216 ScreenMode::Revert()
217 {
218 	if (!fUpdatedMode)
219 		return B_OK;
220 
221 	screen_mode current;
222 	if (Get(current) && fOriginal == current)
223 		return B_OK;
224 
225 	BScreen screen(fWindow);
226 	SetSwapDisplays(&screen, fOriginal.swap_displays);
227 	SetUseLaptopPanel(&screen, fOriginal.use_laptop_panel);
228 	SetTVStandard(&screen, fOriginal.tv_standard);
229 
230 	return screen.SetMode(&fOriginalDisplayMode, true);
231 }
232 
233 
234 void
235 ScreenMode::UpdateOriginalMode()
236 {
237 	BScreen screen(fWindow);
238 	if (screen.GetMode(&fOriginalDisplayMode) == B_OK) {
239 		fUpdatedMode = true;
240 		Get(fOriginal);
241 	}
242 }
243 
244 
245 bool
246 ScreenMode::SupportsColorSpace(screen_mode& mode, color_space space)
247 {
248 	return false;
249 }
250 
251 
252 status_t
253 ScreenMode::GetRefreshLimits(screen_mode& mode, float& min, float& max)
254 {
255 	return B_ERROR;
256 }
257 
258 
259 screen_mode
260 ScreenMode::ModeAt(int32 index)
261 {
262 	if (index < 0)
263 		index = 0;
264 	else if (index >= (int32)fModeCount)
265 		index = fModeCount - 1;
266 
267 	screen_mode mode;
268 	mode.SetTo(fModeList[index]);
269 
270 	return mode;
271 }
272 
273 
274 int32
275 ScreenMode::CountModes()
276 {
277 	return fModeCount;
278 }
279 
280 
281 bool
282 ScreenMode::GetDisplayMode(screen_mode& mode, display_mode& displayMode)
283 {
284 	uint16 virtualWidth, virtualHeight;
285 	int32 bestIndex = -1;
286 	float bestDiff = 999;
287 
288 	virtualWidth = mode.combine == kCombineHorizontally ?
289 		mode.width * 2 : mode.width;
290 	virtualHeight = mode.combine == kCombineVertically ?
291 		mode.height * 2 : mode.height;
292 
293 	// try to find mode in list provided by driver
294 	for (uint32 i = 0; i < fModeCount; i++) {
295 		if (fModeList[i].virtual_width != virtualWidth
296 			|| fModeList[i].virtual_height != virtualHeight
297 			|| (color_space)fModeList[i].space != mode.space)
298 			continue;
299 
300 		float refresh = get_refresh_rate(fModeList[i]);
301 		if (refresh == mode.refresh) {
302 			// we have luck - we can use this mode directly
303 			displayMode = fModeList[i];
304 			displayMode.h_display_start = 0;
305 			displayMode.v_display_start = 0;
306 			return true;
307 		}
308 
309 		float diff = fabs(refresh - mode.refresh);
310 		if (diff < bestDiff) {
311 			bestDiff = diff;
312 			bestIndex = i;
313 		}
314 	}
315 
316 	// we didn't find the exact mode, but something very similar?
317 	if (bestIndex == -1)
318 		return false;
319 
320 	// now, we are better of using GMT formula, but
321 	// as we don't have it, we just tune the pixel
322 	// clock of the best mode.
323 
324 	displayMode = fModeList[bestIndex];
325 	displayMode.h_display_start = 0;
326 	displayMode.v_display_start = 0;
327 
328 	// after some fiddling, it looks like this is the formula
329 	// used by the original panel (notice that / 10 happens before
330 	// multiplying with refresh rate - this leads to different
331 	// rounding)
332 	displayMode.timing.pixel_clock = ((uint32)displayMode.timing.h_total
333 		* displayMode.timing.v_total / 10 * int32(mode.refresh * 10)) / 1000;
334 
335 	return true;
336 }
337 
338