1 /* 2 * Copyright (c) 2001-2008, 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 : fID(id), 44 fDriver(interface ? new DrawingEngine(interface) : NULL), 45 fHWInterface(interface), 46 fIsDefault(true) 47 { 48 } 49 50 51 Screen::Screen() 52 : fID(-1), 53 fDriver(NULL), 54 fHWInterface(NULL), 55 fIsDefault(true) 56 { 57 } 58 59 60 Screen::~Screen() 61 { 62 Shutdown(); 63 delete fDriver; 64 delete fHWInterface; 65 } 66 67 /*! Finds the mode in the mode list that is closest to the mode specified. 68 As long as the mode list is not empty, this method will always succeed. 69 */ 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, bool makeDefault) 93 { 94 display_mode current; 95 GetMode(¤t); 96 // TODO: decide how best to handle the flags here - things like 97 // the screen preflet will generally always set the flags to 0, 98 // while it seems asking the accelerant to automatically pick the 99 // best mode might not necessarily. For the time being, match the 100 // flags before doing the mode comparison in order to prevent 101 // mode switches for otherwise identical modes (this was relatively 102 // easily observed on at least the radeon accelerant - on first boot 103 // the best mode picked included a flag mask of 0xffffffff ; 104 // if you switched the resolution of one workspace to something else 105 // and then back to the resolution it started with, you would observe 106 // a mode switch when jumping between that workspace and the others 107 // that were still using the automatically set default mode) 108 current.flags = mode.flags; 109 if (!memcmp(&mode, ¤t, sizeof(display_mode))) 110 return B_OK; 111 112 gBitmapManager->SuspendOverlays(); 113 114 status_t status = fHWInterface->SetMode(mode); 115 // any attached DrawingEngines will be notified 116 117 gBitmapManager->ResumeOverlays(); 118 119 if (status >= B_OK) 120 fIsDefault = makeDefault; 121 122 return status; 123 } 124 125 126 status_t 127 Screen::SetMode(uint16 width, uint16 height, uint32 colorSpace, 128 const display_timing& timing, bool makeDefault) 129 { 130 display_mode mode; 131 mode.timing = timing; 132 mode.space = colorSpace; 133 mode.virtual_width = width; 134 mode.virtual_height = height; 135 mode.h_display_start = 0; 136 mode.v_display_start = 0; 137 mode.flags = 0; 138 139 return SetMode(mode, makeDefault); 140 } 141 142 143 status_t 144 Screen::SetBestMode(uint16 width, uint16 height, uint32 colorSpace, 145 float frequency, bool strict) 146 { 147 // search for a matching mode 148 display_mode* modes = NULL; 149 uint32 count; 150 status_t status = fHWInterface->GetModeList(&modes, &count); 151 if (status < B_OK) 152 return status; 153 if (count <= 0) 154 return B_ERROR; 155 156 int32 index = _FindBestMode(modes, count, width, height, colorSpace, 157 frequency); 158 if (index < 0) { 159 if (strict) { 160 debug_printf("Finding best mode failed\n"); 161 delete[] modes; 162 return B_ERROR; 163 } else { 164 index = 0; 165 // Just use the first mode in the list 166 } 167 } 168 169 display_mode mode = modes[index]; 170 delete[] modes; 171 172 float modeFrequency = get_mode_frequency(mode); 173 display_mode originalMode = mode; 174 bool adjusted = false; 175 176 if (modeFrequency != frequency) { 177 // adjust timing to fit the requested frequency if needed 178 // (taken from Screen preferences application) 179 mode.timing.pixel_clock = ((uint32)mode.timing.h_total 180 * mode.timing.v_total / 10 * int32(frequency * 10)) / 1000; 181 adjusted = true; 182 } 183 status = SetMode(mode, false); 184 if (status < B_OK && adjusted) { 185 // try again with the unchanged mode 186 status = SetMode(originalMode, false); 187 } 188 189 return status; 190 } 191 192 193 status_t 194 Screen::SetPreferredMode() 195 { 196 display_mode mode; 197 status_t status = fHWInterface->GetPreferredMode(&mode); 198 if (status < B_OK) 199 return status; 200 201 return SetMode(mode, false); 202 } 203 204 205 void 206 Screen::GetMode(display_mode* mode) const 207 { 208 fHWInterface->GetMode(mode); 209 } 210 211 212 void 213 Screen::GetMode(uint16 &width, uint16 &height, uint32 &colorspace, 214 float &frequency) const 215 { 216 display_mode mode; 217 fHWInterface->GetMode(&mode); 218 219 width = mode.virtual_width; 220 height = mode.virtual_height; 221 colorspace = mode.space; 222 frequency = get_mode_frequency(mode); 223 } 224 225 226 status_t 227 Screen::GetMonitorInfo(monitor_info& info) const 228 { 229 return fHWInterface->GetMonitorInfo(&info); 230 } 231 232 233 BRect 234 Screen::Frame() const 235 { 236 display_mode mode; 237 fHWInterface->GetMode(&mode); 238 239 return BRect(0, 0, mode.virtual_width - 1, mode.virtual_height - 1); 240 } 241 242 243 color_space 244 Screen::ColorSpace() const 245 { 246 display_mode mode; 247 fHWInterface->GetMode(&mode); 248 249 return (color_space)mode.space; 250 } 251 252 253 /*! \brief Returns the mode that matches the given criteria best. 254 The "width" argument is the only hard argument, the rest will be adapted 255 as needed. 256 */ 257 int32 258 Screen::_FindBestMode(const display_mode* modes, uint32 count, 259 uint16 width, uint16 height, uint32 colorSpace, float frequency) const 260 { 261 int32 bestDiff = 0; 262 int32 bestIndex = -1; 263 for (uint32 i = 0; i < count; i++) { 264 const display_mode& mode = modes[i]; 265 if (mode.virtual_width != width) 266 continue; 267 268 // compute some random equality score 269 // TODO: check if these scores make sense 270 int32 diff = 1000 * abs(mode.timing.v_display - height) 271 + int32(fabs(get_mode_frequency(mode) - frequency) * 10) 272 + 100 * abs(mode.space - colorSpace); 273 274 if (bestIndex == -1 || diff < bestDiff) { 275 bestDiff = diff; 276 bestIndex = i; 277 } 278 } 279 280 return bestIndex; 281 } 282