2 * Copyright (c) 2007 Gustavo Pichorim Boiko <gustavo.boiko@kdemail.net>
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19 #include "randrscreen.h"
20 #include "randrcrtc.h"
21 #include "randroutput.h"
22 #include "randrmode.h"
25 #include <KConfigGroup>
28 RandRScreen::RandRScreen(int screenIndex
)
31 m_index
= screenIndex
;
32 m_rect
= QRect(0, 0, XDisplayWidth(QX11Info::display(), m_index
),
33 XDisplayHeight(QX11Info::display(), m_index
));
41 // select for randr input events
42 int mask
= RRScreenChangeNotifyMask
|
43 RRCrtcChangeNotifyMask
|
44 RROutputChangeNotifyMask
|
45 RROutputPropertyNotifyMask
;
47 XRRSelectInput(QX11Info::display(), rootWindow(), 0);
48 XRRSelectInput(QX11Info::display(), rootWindow(), mask
);
51 RandRScreen::~RandRScreen()
54 XRRFreeScreenResources(m_resources
);
56 //qDeleteAll(m_crtcs);
57 //qDeleteAll(m_outputs);
58 //qDeleteAll(m_modes);
61 int RandRScreen::index() const
66 XRRScreenResources
* RandRScreen::resources() const
71 Window
RandRScreen::rootWindow() const
73 return RootWindow(QX11Info::display(), m_index
);
76 void RandRScreen::loadSettings(bool notify
)
79 int minW
, minH
, maxW
, maxH
;
81 Status status
= XRRGetScreenSizeRange(QX11Info::display(), rootWindow(),
82 &minW
, &minH
, &maxW
, &maxH
);
83 //FIXME: we should check the status here
85 QSize minSize
= QSize(minW
, minH
);
86 QSize maxSize
= QSize(maxW
, maxH
);
88 if (minSize
!= m_minSize
|| maxSize
!= m_maxSize
)
96 XRRFreeScreenResources(m_resources
);
98 m_resources
= XRRGetScreenResources(QX11Info::display(), rootWindow());
99 Q_ASSERT(m_resources
);
101 RandR::timestamp
= m_resources
->timestamp
;
104 for (int i
= 0; i
< m_resources
->nmode
; ++i
)
106 if (!m_modes
.contains(m_resources
->modes
[i
].id
))
108 m_modes
[m_resources
->modes
[i
].id
] = RandRMode(&m_resources
->modes
[i
]);
114 kDebug() << "Creating CRTC object for XID 0 (\"None\")";
115 RandRCrtc
*c_none
= new RandRCrtc(this, None
);
116 m_crtcs
[None
] = c_none
;
118 for (int i
= 0; i
< m_resources
->ncrtc
; ++i
)
120 if (m_crtcs
.contains(m_resources
->crtcs
[i
]))
121 m_crtcs
[m_resources
->crtcs
[i
]]->loadSettings(notify
);
124 kDebug() << "Creating CRTC object for XID" << m_resources
->crtcs
[i
];
125 RandRCrtc
*c
= new RandRCrtc(this, m_resources
->crtcs
[i
]);
126 connect(c
, SIGNAL(crtcChanged(RRCrtc
, int)), this, SIGNAL(configChanged()));
127 connect(c
, SIGNAL(crtcChanged(RRCrtc
, int)), this, SLOT(save()));
128 c
->loadSettings(notify
);
129 m_crtcs
[m_resources
->crtcs
[i
]] = c
;
135 for (int i
= 0; i
< m_resources
->noutput
; ++i
)
137 if (m_outputs
.contains(m_resources
->outputs
[i
]))
138 ;//m_outputs[m_resources->outputs[i]]->loadSettings(notify);
141 kDebug() << "Creating output object for XID" << m_resources
->outputs
[i
];
142 RandROutput
*o
= new RandROutput(this, m_resources
->outputs
[i
]);
143 connect(o
, SIGNAL(outputChanged(RROutput
, int)), this,
144 SLOT(slotOutputChanged(RROutput
, int)));
145 m_outputs
[m_resources
->outputs
[i
]] = o
;
146 if (o
->isConnected())
155 if (notify
&& changed
)
156 emit
configChanged();
160 void RandRScreen::handleEvent(XRRScreenChangeNotifyEvent
* event
)
162 m_rect
.setWidth(event
->width
);
163 m_rect
.setHeight(event
->height
);
165 emit
configChanged();
168 void RandRScreen::handleRandREvent(XRRNotifyEvent
* event
)
172 XRRCrtcChangeNotifyEvent
*crtcEvent
;
173 XRROutputChangeNotifyEvent
*outputEvent
;
174 XRROutputPropertyNotifyEvent
*propertyEvent
;
176 // forward events to crtcs and outputs
177 switch (event
->subtype
) {
178 case RRNotify_CrtcChange
:
179 crtcEvent
= (XRRCrtcChangeNotifyEvent
*)event
;
180 c
= crtc(crtcEvent
->crtc
);
182 c
->handleEvent(crtcEvent
);
185 case RRNotify_OutputChange
:
186 outputEvent
= (XRROutputChangeNotifyEvent
*)event
;
187 o
= output(outputEvent
->output
);
189 o
->handleEvent(outputEvent
);
192 case RRNotify_OutputProperty
:
193 propertyEvent
= (XRROutputPropertyNotifyEvent
*)event
;
194 o
= output(propertyEvent
->output
);
196 o
->handlePropertyEvent(propertyEvent
);
201 QSize
RandRScreen::minSize() const
206 QSize
RandRScreen::maxSize() const
211 CrtcMap
RandRScreen::crtcs() const
216 RandRCrtc
* RandRScreen::crtc(RRCrtc id
) const
218 if (m_crtcs
.contains(id
))
224 OutputMap
RandRScreen::outputs() const
229 RandROutput
* RandRScreen::output(RROutput id
) const
231 if (m_outputs
.contains(id
))
232 return m_outputs
[id
];
237 ModeMap
RandRScreen::modes() const
242 RandRMode
RandRScreen::mode(RRMode id
) const
244 if (m_modes
.contains(id
))
250 bool RandRScreen::adjustSize(const QRect
&minimumSize
)
252 //try to find a size in which all outputs fit
254 //start with a given minimum rect
255 QRect rect
= QRect(0, 0, 0, 0).united(minimumSize
);
257 foreach(RandROutput
*output
, m_outputs
)
259 // outputs that are not active should not be taken into account
260 // when calculating the screen size
261 if (!output
->isActive())
263 rect
= rect
.united(output
->rect());
268 if (rect
.width() < m_minSize
.width())
269 rect
.setWidth(m_minSize
.width());
270 if (rect
.height() < m_minSize
.height())
271 rect
.setHeight(m_minSize
.height());
273 if (rect
.width() > m_maxSize
.width())
275 if (rect
.height() > m_maxSize
.height())
278 return setSize(rect
.size());
281 bool RandRScreen::setSize(const QSize
&s
)
283 if (s
== m_rect
.size())
286 if (s
.width() < m_minSize
.width() ||
287 s
.height() < m_minSize
.height() ||
288 s
.width() > m_maxSize
.width() ||
289 s
.height() > m_maxSize
.height())
292 int widthMM
, heightMM
;
295 /* values taken from xrandr */
296 dpi
= (25.4 * DisplayHeight(QX11Info::display(), m_index
)) / DisplayHeightMM(QX11Info::display(), m_index
);
297 widthMM
= (int) ((25.4 * s
.width()) / dpi
);
298 heightMM
= (int) ((25.4 * s
.height()) / dpi
);
300 XRRSetScreenSize(QX11Info::display(), rootWindow(), s
.width(), s
.height(), widthMM
, heightMM
);
306 int RandRScreen::connectedCount() const
308 return m_connectedCount
;
311 int RandRScreen::activeCount() const
313 return m_activeCount
;
316 bool RandRScreen::outputsUnified() const
318 return m_outputsUnified
;
321 void RandRScreen::setOutputsUnified(bool unified
)
323 m_outputsUnified
= unified
;
325 // should this be called here?
326 slotUnifyOutputs(unified
);
329 int RandRScreen::unifiedRotations() const
333 int rotations
= RandR::Rotate0
;
335 foreach(RandRCrtc
*crtc
, m_crtcs
)
337 if (!crtc
->connectedOutputs().count())
342 rotations
= crtc
->rotations();
346 rotations
&= crtc
->rotations();
352 SizeList
RandRScreen::unifiedSizes() const
357 foreach(RandROutput
*output
, m_outputs
)
359 if (!output
->isConnected())
364 // we start using the list from the first output
365 sizeList
= output
->sizes();
370 SizeList outputSizes
= output
->sizes();
371 for (int i
= sizeList
.count() - 1; i
>=0; --i
)
373 // check if the current output has the i-th size of the sizeList
374 // if not, remove from the list
375 if (outputSizes
.indexOf(sizeList
[i
]) == -1)
376 sizeList
.removeAt(i
);
384 QRect
RandRScreen::rect() const
389 void RandRScreen::load(KConfig
&config
)
391 KConfigGroup group
= config
.group("Screen_" + QString::number(m_index
));
392 m_outputsUnified
= group
.readEntry("OutputsUnified", false);
393 m_unifiedRect
= (group
.readEntry("UnifiedRect", "0,0,0,0") == "0,0,0,0")
394 ? QRect() // "0,0,0,0" (serialization for QRect()) does not convert to a QRect
395 : group
.readEntry("UnifiedRect", QRect());
396 m_unifiedRotation
= group
.readEntry("UnifiedRotation", (int) RandR::Rotate0
);
398 slotUnifyOutputs(m_outputsUnified
);
400 foreach(RandROutput
*output
, m_outputs
)
402 if (output
->isConnected())
403 output
->load(config
);
407 void RandRScreen::save(KConfig
&config
)
409 KConfigGroup group
= config
.group("Screen_" + QString::number(m_index
));
410 group
.writeEntry("OutputsUnified", m_outputsUnified
);
411 group
.writeEntry("UnifiedRect", m_unifiedRect
);
412 group
.writeEntry("UnifiedRotation", m_unifiedRotation
);
414 foreach(RandROutput
*output
, m_outputs
)
416 if (output
->isConnected())
417 output
->save(config
);
421 void RandRScreen::save()
423 KConfig
cfg("krandrrc");
427 void RandRScreen::load()
429 KConfig
cfg("krandrrc");
433 bool RandRScreen::applyProposed(bool confirm
)
435 kDebug() << "Applying proposed changes for screen" << m_index
<< "...";
440 foreach(RandROutput
*output
, m_outputs
) {
443 RandROutput::Relation outputRelation;
444 RandROutput *related = output->relation(&outputRelation);
447 r.setTopLeft(QPoint(0, 0));
449 QRect relativeRect = related->rect();
451 switch(outputRelation) {
452 case RandROutput::LeftOf:
453 r.setTopLeft(QPoint(relativeRect.x() - r.x(),
455 case RandROutput::RightOf:
456 r.setTopLeft(QPoint(relativeRect.x() + r.x(),
458 case RandROutput::Over:
459 r.setTopLeft(QPoint(relativeRect.x(),
460 relativeRect.y() - r.y()));
461 case RandROutput::Under:
462 r.setTopLeft(QPoint(relativeRect.x(),
463 relativeRect.y() + r.y()));
465 case RandROutput::SameAs:
466 r.setTopLeft(related->rect().topLeft());
469 output->proposeRect(r);
471 if(!output
->applyProposed()) {
477 foreach(RandROutput *output, m_outputs)
480 if (!output->applyProposed())
487 kDebug() << "Changes have been applied to all outputs.";
489 // if we could apply the config clean, ask for confirmation
490 if (succeed
&& confirm
)
491 succeed
= RandR::confirm(r
);
493 // if we succeeded applying and the user confirmed the changes,
494 // just return from here
498 kDebug() << "Changes canceled, reverting to original setup.";
500 //Revert changes if not succeed
501 foreach(RandROutput
*o
, m_outputs
)
503 if (o
->isConnected())
505 o
->proposeOriginal();
512 void RandRScreen::unifyOutputs()
514 KConfig
cfg("krandrrc");
515 SizeList sizes
= unifiedSizes();
517 //FIXME: better handle this
521 // if there is only one output connected, there is no way to unify it
522 if (m_connectedCount
<= 1)
525 if (sizes
.indexOf(m_unifiedRect
.size()) == -1)
526 m_unifiedRect
.setSize(sizes
.first());
528 kDebug() << "Unifying outputs using rect " << m_unifiedRect
;
529 // iterate over all outputs and make sure all connected outputs get activated
530 // and use the right size
531 foreach(RandROutput
*o
, m_outputs
)
533 // if the output is not connected we don't need to do anything
534 if (!o
->isConnected())
537 // if the output is connected and already has the same rect and rotation
538 // as the unified ones, continue
539 if (o
->isActive() && o
->rect() == m_unifiedRect
540 && o
->rotation() == m_unifiedRotation
)
543 // this is to get the refresh rate to use
545 o
->proposeRect(m_unifiedRect
);
546 o
->proposeRotation(m_unifiedRotation
);
547 o
->applyProposed(RandR::ChangeRect
| RandR::ChangeRotation
, false);
550 // FIXME: if by any reason we were not able to unify the outputs, we should
553 emit
configChanged();
556 void RandRScreen::slotResizeUnified(QAction
*action
)
558 m_unifiedRect
.setSize(action
->data().toSize());
562 void RandRScreen::slotUnifyOutputs(bool unified
)
564 m_outputsUnified
= unified
;
565 KConfig
cfg("krandrrc");
567 if (!unified
|| m_connectedCount
<= 1)
569 foreach(RandROutput
*output
, m_outputs
)
570 if (output
->isConnected())
573 output
->applyProposed();
578 SizeList sizes
= unifiedSizes();
582 // FIXME: this should be better handle
586 QSize s
= m_unifiedRect
.size();
588 // if the last size we used is not available, use the first one
590 if (sizes
.indexOf(s
) == -1)
593 m_unifiedRect
.setTopLeft(QPoint(0,0));
594 m_unifiedRect
.setSize(s
);
599 void RandRScreen::slotRotateUnified(QAction
*action
)
601 m_unifiedRotation
= action
->data().toInt();
606 void RandRScreen::slotOutputChanged(RROutput id
, int changes
)
611 int connected
= 0, active
= 0;
612 foreach(RandROutput
*output
, m_outputs
)
614 if (output
->isConnected())
616 if (output
->isActive())
620 m_connectedCount
= connected
;
621 m_activeCount
= active
;
623 // if there is less than 2 outputs connected, there is no need to unify
627 // wait some time before checking the output configuration as some randr
628 // clients do operations in more than one step
633 #include "randrscreen.moc"