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