* Copyright 2005-2011, Haiku.
* Distributed under the terms of the MIT License.
*
* Authors:
* Axel Dörfler, axeld@pinc-software.de
*/
#include "ScreenMode.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <InterfaceDefs.h>
#include <String.h>
#include <compute_display_timing.h>
* It's a solution that works with the current BeOS interface that Haiku
* adopted.
* However, it's not a nice and clean solution. Don't use this header in any
* application if you can avoid it. No other driver is using this, or should
* be using this.
* It will be replaced as soon as we introduce an updated accelerant interface
* which may even happen before R1 hits the streets.
*/
#include "multimon.h"
* wget https://uefi.org/uefi-pnp-export -O Vendors.h.tmp
* if [ $? -eq 0 ]; then
* sed -E -e 's:<thead>::g' -e 's:<tr.*="(.+)"><td>:{ ":g' -e 's:<td>[[:digit:]]{2}/[[:digit:]]{2}/[[:digit:]]{4}</td>::g' -e 's: *(<\/td><td>):\", \":g' -e 's: *<\/td>.<\/tr>:\" },:g' -e 's/"(.*?)", "(.*?)"/\"\2\", \"\1\"/' -e 's/&/\&/' -e "s/'/'/" Vendors.h.tmp | grep -v '<' | sort > Vendors.h
* fi
* rm Vendors.h.tmp
*/
struct pnp_id {
const char* id;
const char* manufacturer;
bool operator==(const pnp_id& a) const {
return ::strcasecmp(a.id, id) == 0;
};
bool operator()(const pnp_id& a, const pnp_id& b) const {
return ::strcasecmp(a.id, b.id) < 0;
};
};
static const struct pnp_id kPNPIDs[] = {
#include "Vendors.h"
};
static combine_mode
get_combine_mode(const display_mode& mode)
{
if ((mode.flags & B_SCROLL) == 0)
return kCombineDisable;
if (mode.virtual_width == mode.timing.h_display * 2)
return kCombineHorizontally;
if (mode.virtual_height == mode.timing.v_display * 2)
return kCombineVertically;
return kCombineDisable;
}
static float
get_refresh_rate(const display_mode& mode)
{
return rint(10 * float(mode.timing.pixel_clock * 1000)
/ float(mode.timing.h_total * mode.timing.v_total)) / 10.0;
}
static int
compare_mode(const void* _mode1, const void* _mode2)
{
display_mode *mode1 = (display_mode *)_mode1;
display_mode *mode2 = (display_mode *)_mode2;
combine_mode combine1, combine2;
uint16 width1, width2, height1, height2;
combine1 = get_combine_mode(*mode1);
combine2 = get_combine_mode(*mode2);
width1 = mode1->virtual_width;
height1 = mode1->virtual_height;
width2 = mode2->virtual_width;
height2 = mode2->virtual_height;
if (combine1 == kCombineHorizontally)
width1 /= 2;
if (combine1 == kCombineVertically)
height1 /= 2;
if (combine2 == kCombineHorizontally)
width2 /= 2;
if (combine2 == kCombineVertically)
height2 /= 2;
if (width1 != width2)
return width1 - width2;
if (height1 != height2)
return height1 - height2;
return (int)(10 * get_refresh_rate(*mode1)
- 10 * get_refresh_rate(*mode2));
}
int32
screen_mode::BitsPerPixel() const
{
switch (space) {
case B_RGB32: return 32;
case B_RGB24: return 24;
case B_RGB16: return 16;
case B_RGB15: return 15;
case B_CMAP8: return 8;
default: return 0;
}
}
bool
screen_mode::operator==(const screen_mode &other) const
{
return !(*this != other);
}
bool
screen_mode::operator!=(const screen_mode &other) const
{
return width != other.width || height != other.height
|| space != other.space || refresh != other.refresh
|| combine != other.combine
|| swap_displays != other.swap_displays
|| use_laptop_panel != other.use_laptop_panel
|| tv_standard != other.tv_standard;
}
void
screen_mode::SetTo(const display_mode& mode)
{
width = mode.virtual_width;
height = mode.virtual_height;
space = (color_space)mode.space;
combine = get_combine_mode(mode);
refresh = get_refresh_rate(mode);
if (combine == kCombineHorizontally)
width /= 2;
else if (combine == kCombineVertically)
height /= 2;
swap_displays = false;
use_laptop_panel = false;
tv_standard = 0;
}
ScreenMode::ScreenMode(BWindow* window)
:
fWindow(window),
fUpdatedModes(false)
{
BScreen screen(window);
if (screen.GetModeList(&fModeList, &fModeCount) == B_OK) {
qsort(fModeList, fModeCount, sizeof(display_mode), compare_mode);
} else {
fModeList = NULL;
fModeCount = 0;
}
}
ScreenMode::~ScreenMode()
{
free(fModeList);
}
status_t
ScreenMode::Set(const screen_mode& mode, int32 workspace)
{
if (!fUpdatedModes)
UpdateOriginalModes();
BScreen screen(fWindow);
if (workspace == ~0)
workspace = current_workspace();
SetSwapDisplays(&screen, mode.swap_displays);
SetUseLaptopPanel(&screen, mode.use_laptop_panel);
SetTVStandard(&screen, mode.tv_standard);
display_mode displayMode;
if (!_GetDisplayMode(mode, displayMode))
return B_ENTRY_NOT_FOUND;
return screen.SetMode(workspace, &displayMode, true);
}
status_t
ScreenMode::Get(screen_mode& mode, int32 workspace) const
{
display_mode displayMode;
BScreen screen(fWindow);
if (workspace == ~0)
workspace = current_workspace();
if (screen.GetMode(workspace, &displayMode) != B_OK)
return B_ERROR;
mode.SetTo(displayMode);
if (GetSwapDisplays(&screen, &mode.swap_displays) != B_OK)
mode.swap_displays = false;
if (GetUseLaptopPanel(&screen, &mode.use_laptop_panel) != B_OK)
mode.use_laptop_panel = false;
if (GetTVStandard(&screen, &mode.tv_standard) != B_OK)
mode.tv_standard = 0;
return B_OK;
}
status_t
ScreenMode::GetOriginalMode(screen_mode& mode, int32 workspace) const
{
if (workspace == ~0)
workspace = current_workspace();
else if (workspace > 31)
return B_BAD_INDEX;
mode = fOriginal[workspace];
return B_OK;
}
status_t
ScreenMode::Set(const display_mode& mode, int32 workspace)
{
if (!fUpdatedModes)
UpdateOriginalModes();
BScreen screen(fWindow);
if (workspace == ~0)
workspace = current_workspace();
display_mode nonConstMode;
memcpy(&nonConstMode, &mode, sizeof(display_mode));
return screen.SetMode(workspace, &nonConstMode, true);
}
status_t
ScreenMode::Get(display_mode& mode, int32 workspace) const
{
BScreen screen(fWindow);
if (workspace == ~0)
workspace = current_workspace();
return screen.GetMode(workspace, &mode);
}
of workspaces.
*/
status_t
ScreenMode::Revert()
{
if (!fUpdatedModes)
return B_ERROR;
status_t result = B_OK;
screen_mode current;
for (int32 workspace = 0; workspace < count_workspaces(); workspace++) {
if (Get(current, workspace) == B_OK && fOriginal[workspace] == current)
continue;
BScreen screen(fWindow);
if (workspace == current_workspace()) {
SetSwapDisplays(&screen, fOriginal[workspace].swap_displays);
SetUseLaptopPanel(&screen, fOriginal[workspace].use_laptop_panel);
SetTVStandard(&screen, fOriginal[workspace].tv_standard);
}
result = screen.SetMode(workspace, &fOriginalDisplayMode[workspace],
true);
if (result != B_OK)
break;
}
return result;
}
void
ScreenMode::UpdateOriginalModes()
{
BScreen screen(fWindow);
for (int32 workspace = 0; workspace < count_workspaces(); workspace++) {
if (screen.GetMode(workspace, &fOriginalDisplayMode[workspace])
== B_OK) {
Get(fOriginal[workspace], workspace);
fUpdatedModes = true;
}
}
}
bool
ScreenMode::SupportsColorSpace(const screen_mode& mode, color_space space)
{
return true;
}
status_t
ScreenMode::GetRefreshLimits(const screen_mode& mode, float& min, float& max)
{
uint32 minClock, maxClock;
display_mode displayMode;
if (!_GetDisplayMode(mode, displayMode))
return B_ERROR;
BScreen screen(fWindow);
if (screen.GetPixelClockLimits(&displayMode, &minClock, &maxClock) < B_OK)
return B_ERROR;
uint32 total = displayMode.timing.h_total * displayMode.timing.v_total;
min = minClock * 1000.0 / total;
max = maxClock * 1000.0 / total;
return B_OK;
}
const char*
ScreenMode::GetManufacturerFromID(const char* id) const
{
const size_t numElements = B_COUNT_OF(kPNPIDs);
const struct pnp_id key = { id, "dummy" };
const pnp_id* lastElement = kPNPIDs + numElements;
const pnp_id* element = std::find(kPNPIDs, lastElement, key);
if (element == lastElement) {
return NULL;
}
return element->manufacturer;
}
status_t
ScreenMode::GetMonitorInfo(monitor_info& info, float* _diagonalInches)
{
BScreen screen(fWindow);
status_t status = screen.GetMonitorInfo(&info);
if (status != B_OK)
return status;
if (_diagonalInches != NULL) {
*_diagonalInches = round(sqrt(info.width * info.width
+ info.height * info.height) / 0.254) / 10.0;
}
if (info.min_vertical_frequency == 0) {
info.min_vertical_frequency = 60;
info.max_vertical_frequency = 85;
}
char vendor[4];
strlcpy(vendor, info.vendor, sizeof(vendor));
const char* vendorString = GetManufacturerFromID(vendor);
if (vendorString != NULL)
strlcpy(info.vendor, vendorString, sizeof(info.vendor));
BString name(info.name);
name.IReplaceAll(info.vendor, "");
name.Trim();
strcpy(info.name, name.String());
return B_OK;
}
status_t
ScreenMode::GetDeviceInfo(accelerant_device_info& info)
{
BScreen screen(fWindow);
return screen.GetDeviceInfo(&info);
}
screen_mode
ScreenMode::ModeAt(int32 index)
{
screen_mode mode;
mode.SetTo(DisplayModeAt(index));
return mode;
}
const display_mode&
ScreenMode::DisplayModeAt(int32 index)
{
if (index < 0)
index = 0;
else if (index >= (int32)fModeCount)
index = fModeCount - 1;
return fModeList[index];
}
int32
ScreenMode::CountModes()
{
return fModeCount;
}
find a matching mode, it will compute the mode manually using the GTF.
*/
bool
ScreenMode::_GetDisplayMode(const screen_mode& mode, display_mode& displayMode)
{
uint16 virtualWidth, virtualHeight;
int32 bestIndex = -1;
float bestDiff = 999;
virtualWidth = mode.combine == kCombineHorizontally
? mode.width * 2 : mode.width;
virtualHeight = mode.combine == kCombineVertically
? mode.height * 2 : mode.height;
for (uint32 i = 0; i < fModeCount; i++) {
if (fModeList[i].virtual_width != virtualWidth
|| fModeList[i].virtual_height != virtualHeight
|| (color_space)fModeList[i].space != mode.space)
continue;
float refreshDiff = fabs(get_refresh_rate(fModeList[i]) - mode.refresh);
if (refreshDiff < 0.006 * mode.refresh) {
displayMode = fModeList[i];
displayMode.h_display_start = 0;
displayMode.v_display_start = 0;
displayMode.timing.pixel_clock = uint32(((displayMode.timing.h_total
* displayMode.timing.v_total * mode.refresh) / 1000.0) + 0.5);
return true;
}
if (refreshDiff < bestDiff) {
bestDiff = refreshDiff;
bestIndex = i;
}
}
if (bestIndex == -1)
return false;
displayMode = fModeList[bestIndex];
displayMode.h_display_start = 0;
displayMode.v_display_start = 0;
compute_display_timing(mode.width, mode.height, mode.refresh, false,
&displayMode.timing);
return true;
}