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