mirror of
https://bitbucket.org/smil3y/kde-workspace.git
synced 2025-02-24 19:02:51 +00:00
423 lines
12 KiB
C++
423 lines
12 KiB
C++
/*
|
|
* Copyright 2008 Aike J Sommer <dev@aikesommer.name>
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as
|
|
* published by the Free Software Foundation; either version 2,
|
|
* or (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details
|
|
*
|
|
* You should have received a copy of the GNU Library General Public
|
|
* License along with this program; if not, write to the
|
|
* Free Software Foundation, Inc.,
|
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
*/
|
|
|
|
|
|
#include "xrandroutputs.h"
|
|
#include "edid.h"
|
|
|
|
#include "xrandr12/randrscreen.h"
|
|
#include "xrandr12/randroutput.h"
|
|
|
|
#include <X11/Xatom.h>
|
|
|
|
#include <QtGui/qx11info_x11.h>
|
|
|
|
#include <KDebug>
|
|
#include <QtGui/qx11info_x11.h>
|
|
|
|
namespace Kephal {
|
|
|
|
XRandROutputs::XRandROutputs(QObject * parent, RandRDisplay * display)
|
|
: BackendOutputs(parent)
|
|
{
|
|
m_display = display;
|
|
init();
|
|
}
|
|
|
|
QList<Output *> XRandROutputs::outputs() {
|
|
QList<Output *> result;
|
|
foreach (XRandROutput * output, m_outputs) {
|
|
result.append(output);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
void XRandROutputs::init() {
|
|
kDebug();
|
|
RandRScreen * screen = m_display->screen(0);
|
|
foreach (RandROutput * output, screen->outputs()) {
|
|
XRandROutput * o = new XRandROutput(this, output->id());
|
|
|
|
connect(o, SIGNAL(outputConnected(Kephal::Output*)),
|
|
this, SIGNAL(outputConnected(Kephal::Output*)));
|
|
|
|
connect(o, SIGNAL(outputDisconnected(Kephal::Output*)),
|
|
this, SIGNAL(outputDisconnected(Kephal::Output*)));
|
|
|
|
connect(o, SIGNAL(outputActivated(Kephal::Output*)),
|
|
this, SIGNAL(outputActivated(Kephal::Output*)));
|
|
|
|
connect(o, SIGNAL(outputDeactivated(Kephal::Output*)),
|
|
this, SIGNAL(outputDeactivated(Kephal::Output*)));
|
|
|
|
connect(o, SIGNAL(outputResized(Kephal::Output*,QSize,QSize)),
|
|
this, SIGNAL(outputResized(Kephal::Output*,QSize,QSize)));
|
|
|
|
connect(o, SIGNAL(outputMoved(Kephal::Output*,QPoint,QPoint)),
|
|
this, SIGNAL(outputMoved(Kephal::Output*,QPoint,QPoint)));
|
|
|
|
connect(o, SIGNAL(outputRateChanged(Kephal::Output*,float,float)),
|
|
this, SIGNAL(outputRateChanged(Kephal::Output*,float,float)));
|
|
|
|
connect(o, SIGNAL(outputRotated(Kephal::Output*,Kephal::Rotation,Kephal::Rotation)),
|
|
this, SIGNAL(outputRotated(Kephal::Output*,Kephal::Rotation,Kephal::Rotation)));
|
|
connect(o, SIGNAL(outputReflected(Kephal::Output*,bool,bool,bool,bool)),
|
|
this, SIGNAL(outputReflected(Kephal::Output*,bool,bool,bool,bool)));
|
|
|
|
kDebug() << " added output " << output->id();
|
|
m_outputs.insert(o->id(), o);
|
|
}
|
|
}
|
|
|
|
RandROutput * XRandROutputs::output(RROutput rrId) {
|
|
return m_display->screen(0)->outputs()[rrId];
|
|
}
|
|
|
|
RandRDisplay * XRandROutputs::display() {
|
|
return m_display;
|
|
}
|
|
|
|
XRandROutput::XRandROutput(XRandROutputs * parent, RROutput rrId)
|
|
: BackendOutput(parent), m_productId(-1), m_serialNumber(0)
|
|
{
|
|
m_outputs = parent;
|
|
m_rrId = rrId;
|
|
|
|
parseEdid();
|
|
|
|
saveAsPrevious();
|
|
|
|
connect(output(), SIGNAL(outputChanged(RROutput,int)),
|
|
this, SLOT(outputChanged(RROutput,int)));
|
|
//connect(this, SLOT(_activate()), output(), SLOT(slotEnable()));
|
|
//connect(this, SLOT(_deactivate()), output(), SLOT(slotDisable()));
|
|
}
|
|
|
|
void XRandROutput::parseEdid() {
|
|
|
|
Atom atom = XInternAtom (QX11Info::display(), "EDID_DATA", false);
|
|
Atom type;
|
|
unsigned char * data;
|
|
unsigned long size;
|
|
unsigned long after;
|
|
int format;
|
|
|
|
XRRGetOutputProperty(QX11Info::display(), m_rrId, atom, 0, 100,
|
|
False, False, AnyPropertyType,
|
|
&type, &format, &size, &after, &data);
|
|
|
|
if (type == XA_INTEGER && format == 8 && EDID_TEST_HEADER(data)) {
|
|
//kDebug() << "got a valid edid block...";
|
|
|
|
/**
|
|
* parse the 3 letter vendor code
|
|
*/
|
|
char * vendor = new char[4];
|
|
|
|
vendor[0] = EDID_VENDOR_1(data);
|
|
vendor[1] = EDID_VENDOR_2(data);
|
|
vendor[2] = EDID_VENDOR_3(data);
|
|
vendor[3] = 0x00;
|
|
m_vendor = vendor;
|
|
|
|
kDebug() << "vendor code:" << m_vendor;
|
|
|
|
delete[] vendor;
|
|
|
|
/**
|
|
* parse the 16bit product id
|
|
*/
|
|
m_productId = EDID_PRODUCT_ID(data);
|
|
|
|
kDebug() << "product id:" << m_productId;
|
|
|
|
/**
|
|
* parse the 32bit serial number
|
|
*/
|
|
m_serialNumber = EDID_SERIAL_NUMBER(data);
|
|
|
|
kDebug() << "serial number:" << m_serialNumber;
|
|
} else {
|
|
m_vendor = QString();
|
|
m_productId = -1;
|
|
m_serialNumber = 0;
|
|
}
|
|
|
|
XFree(data);
|
|
}
|
|
|
|
void XRandROutput::outputChanged(RROutput id, int changes) {
|
|
Q_ASSERT(id == m_rrId);
|
|
kDebug() << isConnected() << isActivated() << geom();
|
|
if (isConnected() != m_previousConnected) {
|
|
if (isConnected()) {
|
|
saveAsPrevious();
|
|
parseEdid();
|
|
emit outputConnected(this);
|
|
if (isActivated()) {
|
|
emit outputActivated(this);
|
|
}
|
|
} else {
|
|
if (m_previousActivated) {
|
|
saveAsPrevious();
|
|
emit outputDeactivated(this);
|
|
}
|
|
saveAsPrevious();
|
|
emit outputDisconnected(this);
|
|
}
|
|
return;
|
|
}
|
|
if (! isConnected()) {
|
|
return;
|
|
}
|
|
if (isActivated() != m_previousActivated) {
|
|
saveAsPrevious();
|
|
if (isActivated()) {
|
|
emit outputActivated(this);
|
|
} else {
|
|
emit outputDeactivated(this);
|
|
}
|
|
return;
|
|
}
|
|
|
|
QRect previousGeom = m_previousGeom;
|
|
Rotation previousRotation = m_previousRotation;
|
|
float previousRate = m_previousRate;
|
|
bool previousReflectX = m_previousReflectX;
|
|
bool previousReflectY = m_previousReflectY;
|
|
saveAsPrevious();
|
|
if (size() != previousGeom.size()) {
|
|
emit outputResized(this, previousGeom.size(), size());
|
|
}
|
|
if (position() != previousGeom.topLeft()) {
|
|
emit outputMoved(this, previousGeom.topLeft(), position());
|
|
}
|
|
if (rotation() != previousRotation) {
|
|
emit outputRotated(this, previousRotation, rotation());
|
|
}
|
|
if (rate() != previousRate) {
|
|
emit outputRateChanged(this, previousRate, rate());
|
|
}
|
|
if ((reflectX() != previousReflectX) || (reflectY() != previousReflectY)) {
|
|
emit outputReflected(this, previousReflectX, previousReflectY, reflectX(), reflectY());
|
|
}
|
|
}
|
|
|
|
void XRandROutput::saveAsPrevious() {
|
|
m_previousConnected = isConnected();
|
|
m_previousActivated = isActivated();
|
|
m_previousGeom = geom();
|
|
m_previousRotation = rotation();
|
|
m_previousRate = rate();
|
|
m_previousReflectX = reflectX();
|
|
m_previousReflectY = reflectY();
|
|
}
|
|
|
|
bool XRandROutput::applyGeom(const QRect & geom, float rate) {
|
|
if ((geom == this->geom()) && ((rate < 1) || (qFuzzyCompare(rate, this->rate())))) {
|
|
return true;
|
|
}
|
|
|
|
output()->proposeRect(geom);
|
|
if (rate < 1) {
|
|
rate = output()->refreshRate();
|
|
}
|
|
bool found = false;
|
|
QList<float> rates = output()->refreshRates(geom.size());
|
|
foreach (float r, rates) {
|
|
if (qFuzzyCompare(rate, r)) {
|
|
rate = r;
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
if ((! found) && (! rates.empty())) {
|
|
rate = rates[0];
|
|
}
|
|
if (rate > 1) {
|
|
output()->proposeRefreshRate(rate);
|
|
}
|
|
|
|
return output()->applyProposed();
|
|
}
|
|
|
|
bool XRandROutput::applyOrientation(Rotation rotation, bool reflectX, bool reflectY) {
|
|
if ((rotation == this->rotation()) && (reflectX == this->reflectX()) && (reflectY == this->reflectY())) {
|
|
return true;
|
|
}
|
|
|
|
int orientation = 0;
|
|
|
|
switch (rotation) {
|
|
case RotateRight:
|
|
orientation |= RandR::Rotate90;
|
|
break;
|
|
case RotateLeft:
|
|
orientation |= RandR::Rotate270;
|
|
break;
|
|
case RotateInverted:
|
|
orientation |= RandR::Rotate180;
|
|
break;
|
|
default:
|
|
orientation |= RandR::Rotate0;
|
|
}
|
|
|
|
if (reflectX) {
|
|
orientation |= RandR::ReflectX;
|
|
}
|
|
if (reflectY) {
|
|
orientation |= RandR::ReflectY;
|
|
}
|
|
|
|
output()->proposeRotation(orientation);
|
|
return output()->applyProposed();
|
|
}
|
|
|
|
void XRandROutput::deactivate()
|
|
{
|
|
output()->slotDisable();
|
|
}
|
|
|
|
RandROutput * XRandROutput::output() const
|
|
{
|
|
return m_outputs->output(m_rrId);
|
|
}
|
|
|
|
QString XRandROutput::id() const
|
|
{
|
|
return output()->name();
|
|
}
|
|
|
|
QSize XRandROutput::size() const
|
|
{
|
|
return output()->rect().size();
|
|
}
|
|
|
|
QSize XRandROutput::preferredSize() const
|
|
{
|
|
if (output()->preferredMode().size().isEmpty()) {
|
|
return QSize();
|
|
} else {
|
|
return output()->preferredMode().size();
|
|
}
|
|
}
|
|
|
|
QList<QSize> XRandROutput::availableSizes() const {
|
|
QList<QSize> sizes = output()->sizes();
|
|
return sizes;
|
|
}
|
|
|
|
QPoint XRandROutput::position() const {
|
|
return output()->rect().topLeft();
|
|
}
|
|
|
|
bool XRandROutput::isConnected() const {
|
|
return output()->isConnected();
|
|
}
|
|
|
|
bool XRandROutput::isActivated() const
|
|
{
|
|
return output()->isActive();
|
|
}
|
|
|
|
QString XRandROutput::vendor() const
|
|
{
|
|
return m_vendor;
|
|
}
|
|
|
|
int XRandROutput::productId() const
|
|
{
|
|
return m_productId;
|
|
}
|
|
|
|
unsigned int XRandROutput::serialNumber() const
|
|
{
|
|
return m_serialNumber;
|
|
}
|
|
|
|
RROutput XRandROutput::_id() const {
|
|
return m_rrId;
|
|
}
|
|
|
|
Rotation XRandROutput::rotation() const
|
|
{
|
|
switch (output()->rotation() & RandR::RotateMask) {
|
|
case RandR::Rotate90:
|
|
return RotateRight;
|
|
case RandR::Rotate180:
|
|
return RotateInverted;
|
|
case RandR::Rotate270:
|
|
return RotateLeft;
|
|
default:
|
|
return RotateNormal;
|
|
}
|
|
}
|
|
|
|
bool XRandROutput::reflectX() const
|
|
{
|
|
return (output()->rotation() & RandR::ReflectX);
|
|
}
|
|
|
|
bool XRandROutput::reflectY() const
|
|
{
|
|
return (output()->rotation() & RandR::ReflectY);
|
|
}
|
|
|
|
float XRandROutput::rate() const
|
|
{
|
|
return output()->refreshRate();
|
|
}
|
|
|
|
QList<float> XRandROutput::availableRates() const
|
|
{
|
|
return output()->refreshRates(size());
|
|
}
|
|
|
|
void XRandROutput::resize(const QSize & size)
|
|
{
|
|
Q_UNUSED(size)
|
|
#warning implement!
|
|
}
|
|
|
|
void XRandROutput::rotate(Rotation rotation)
|
|
{
|
|
Q_UNUSED(rotation)
|
|
#warning implement!
|
|
}
|
|
|
|
void XRandROutput::setReflectX(bool reflect)
|
|
{
|
|
Q_UNUSED(reflect)
|
|
#warning implement!
|
|
}
|
|
|
|
void XRandROutput::setReflectY(bool reflect)
|
|
{
|
|
Q_UNUSED(reflect)
|
|
#warning implement!
|
|
}
|
|
|
|
void XRandROutput::changeRate(double rate)
|
|
{
|
|
Q_UNUSED(rate)
|
|
#warning implement!
|
|
}
|
|
|
|
}
|
|
|