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