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