1 /* 2 * Copyright 2001-2013, 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 : 44 fID(id), 45 fDriver(interface ? interface->CreateDrawingEngine() : NULL), 46 fHWInterface(interface) 47 { 48 } 49 50 51 Screen::Screen() 52 : 53 fID(-1) 54 { 55 } 56 57 58 Screen::~Screen() 59 { 60 Shutdown(); 61 } 62 63 64 /*! Finds the mode in the mode list that is closest to the mode specified. 65 As long as the mode list is not empty, this method will always succeed. 66 */ 67 status_t 68 Screen::Initialize() 69 { 70 status_t status = B_NO_INIT; 71 72 if (fHWInterface.IsSet()) { 73 // init the graphics hardware 74 status = fHWInterface->Initialize(); 75 } 76 77 // Turn on screen if this is not yet done by BIOS 78 if (status == B_OK) 79 fHWInterface->SetDPMSMode(B_DPMS_ON); 80 81 82 return status; 83 } 84 85 86 void 87 Screen::Shutdown() 88 { 89 if (fHWInterface.IsSet()) 90 fHWInterface->Shutdown(); 91 } 92 93 94 status_t 95 Screen::SetMode(const display_mode& mode) 96 { 97 display_mode current; 98 GetMode(current); 99 if (!memcmp(&mode, ¤t, sizeof(display_mode))) 100 return B_OK; 101 102 gBitmapManager->SuspendOverlays(); 103 104 status_t status = fHWInterface->SetMode(mode); 105 // any attached DrawingEngines will be notified 106 107 gBitmapManager->ResumeOverlays(); 108 109 return status; 110 } 111 112 113 status_t 114 Screen::SetMode(uint16 width, uint16 height, uint32 colorSpace, 115 const display_timing& timing) 116 { 117 display_mode mode; 118 mode.timing = timing; 119 mode.space = colorSpace; 120 mode.virtual_width = width; 121 mode.virtual_height = height; 122 mode.h_display_start = 0; 123 mode.v_display_start = 0; 124 mode.flags = 0; 125 126 return SetMode(mode); 127 } 128 129 130 status_t 131 Screen::SetBestMode(uint16 width, uint16 height, uint32 colorSpace, 132 float frequency, bool strict) 133 { 134 // search for a matching mode 135 display_mode* modes = NULL; 136 uint32 count; 137 status_t status = fHWInterface->GetModeList(&modes, &count); 138 if (status < B_OK) 139 return status; 140 if (count <= 0) 141 return B_ERROR; 142 143 int32 index = _FindBestMode(modes, count, width, height, colorSpace, 144 frequency); 145 if (index < 0) { 146 debug_printf("app_server: Finding best mode for %ux%u (%" B_PRIu32 147 ", %g Hz%s) failed\n", width, height, colorSpace, frequency, 148 strict ? ", strict" : ""); 149 150 if (strict) { 151 delete[] modes; 152 return B_ERROR; 153 } else { 154 index = 0; 155 // Just use the first mode in the list 156 debug_printf("app_server: Use %ux%u (%" B_PRIu32 ") instead.\n", 157 modes[0].timing.h_total, modes[0].timing.v_total, modes[0].space); 158 } 159 } 160 161 display_mode mode = modes[index]; 162 delete[] modes; 163 164 float modeFrequency = get_mode_frequency(mode); 165 display_mode originalMode = mode; 166 bool adjusted = false; 167 168 if (modeFrequency != frequency) { 169 // adjust timing to fit the requested frequency if needed 170 // (taken from Screen preferences application) 171 mode.timing.pixel_clock = ((uint32)mode.timing.h_total 172 * mode.timing.v_total / 10 * int32(frequency * 10)) / 1000; 173 adjusted = true; 174 } 175 status = SetMode(mode); 176 if (status != B_OK && adjusted) { 177 // try again with the unchanged mode 178 status = SetMode(originalMode); 179 } 180 181 return status; 182 } 183 184 185 status_t 186 Screen::SetPreferredMode() 187 { 188 display_mode mode; 189 status_t status = fHWInterface->GetPreferredMode(&mode); 190 if (status != B_OK) 191 return status; 192 193 return SetMode(mode); 194 } 195 196 197 void 198 Screen::GetMode(display_mode& mode) const 199 { 200 fHWInterface->GetMode(&mode); 201 } 202 203 204 void 205 Screen::GetMode(uint16 &width, uint16 &height, uint32 &colorspace, 206 float &frequency) const 207 { 208 display_mode mode; 209 fHWInterface->GetMode(&mode); 210 211 width = mode.virtual_width; 212 height = mode.virtual_height; 213 colorspace = mode.space; 214 frequency = get_mode_frequency(mode); 215 } 216 217 218 status_t 219 Screen::GetMonitorInfo(monitor_info& info) const 220 { 221 return fHWInterface->GetMonitorInfo(&info); 222 } 223 224 225 void 226 Screen::SetFrame(const BRect& rect) 227 { 228 // TODO: multi-monitor support... 229 } 230 231 232 BRect 233 Screen::Frame() const 234 { 235 display_mode mode; 236 fHWInterface->GetMode(&mode); 237 238 return BRect(0, 0, mode.virtual_width - 1, mode.virtual_height - 1); 239 } 240 241 242 color_space 243 Screen::ColorSpace() const 244 { 245 display_mode mode; 246 fHWInterface->GetMode(&mode); 247 248 return (color_space)mode.space; 249 } 250 251 252 /*! \brief Returns the mode that matches the given criteria best. 253 The "width" argument is the only hard argument, the rest will be adapted 254 as needed. 255 */ 256 int32 257 Screen::_FindBestMode(const display_mode* modes, uint32 count, 258 uint16 width, uint16 height, uint32 colorSpace, float frequency) const 259 { 260 int32 bestDiff = 0; 261 int32 bestIndex = -1; 262 for (uint32 i = 0; i < count; i++) { 263 const display_mode& mode = modes[i]; 264 if (mode.virtual_width != width) 265 continue; 266 267 // compute some random equality score 268 // TODO: check if these scores make sense 269 int32 diff = 1000 * abs(mode.timing.v_display - height) 270 + int32(fabs(get_mode_frequency(mode) - frequency) * 10) 271 + 100 * abs((int)(mode.space - colorSpace)); 272 273 if (bestIndex == -1 || diff < bestDiff) { 274 bestDiff = diff; 275 bestIndex = i; 276 } 277 } 278 279 return bestIndex; 280 } 281