2 * Copyright (c) 2007 Gustavo Pichorim Boiko <gustavo.boiko@kdemail.net>
3 * Copyright (c) 2007, 2008 Harry Bock <hbock@providence.edu>
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 #include "randroutput.h"
21 #include "randrscreen.h"
22 #include "randrcrtc.h"
23 #include "randrmode.h"
26 #include <KConfigGroup>
30 RandROutput::RandROutput(RandRScreen
*parent
, RROutput id
)
42 m_proposedRotation
= m_originalRotation
;
43 m_proposedRate
= m_originalRate
;
44 m_proposedRect
= m_originalRect
;
47 RandROutput::~RandROutput()
51 RROutput
RandROutput::id() const
56 RandRScreen
*RandROutput::screen() const
61 void RandROutput::queryOutputInfo(void)
63 XRROutputInfo
*info
= XRRGetOutputInfo(QX11Info::display(), m_screen
->resources(), m_id
);
66 if (RandR::timestamp
!= info
->timestamp
)
67 RandR::timestamp
= info
->timestamp
;
69 // Set up the output's connection status, name, and current
71 m_connected
= (info
->connection
== RR_Connected
);
74 kDebug() << "XID" << m_id
<< "is output" << m_name
<<
75 (isConnected() ? "(connected)" : "(disconnected)");
77 setCrtc(m_screen
->crtc(info
->crtc
));
78 kDebug() << "Possible CRTCs for output" << m_name
<< ":";
81 kDebug() << " - none";
83 for(int i
= 0; i
< info
->ncrtc
; ++i
) {
84 kDebug() << " - CRTC" << info
->crtcs
[i
];
85 m_possibleCrtcs
.append(info
->crtcs
[i
]);
88 //TODO: is it worth notifying changes on mode list changing?
91 for (int i
= 0; i
< info
->nmode
; ++i
) {
92 if (i
< info
->npreferred
) {
93 m_preferredMode
= m_screen
->mode(info
->modes
[i
]);
95 m_modes
.append(info
->modes
[i
]);
98 //get all possible rotations
100 for (int i
= 0; i
< m_possibleCrtcs
.count(); ++i
)
102 RandRCrtc
*crtc
= m_screen
->crtc(m_possibleCrtcs
.at(i
));
104 m_rotations
|= crtc
->rotations();
106 m_originalRotation
= m_crtc
->rotation();
107 m_originalRate
= m_crtc
->refreshRate();
108 m_originalRect
= m_crtc
->rect();
111 kDebug() << "Current configuration for output" << m_name
<< ":";
112 kDebug() << " - Refresh rate:" << m_originalRate
;
113 kDebug() << " - Rect:" << m_originalRect
;
114 kDebug() << " - Rotation:" << m_originalRotation
;
117 XRRFreeOutputInfo(info
);
120 void RandROutput::loadSettings(bool notify
)
125 kDebug() << "STUB: calling queryOutputInfo instead. Check if this has "
126 "any undesired effects. ";
131 XRROutputInfo *info = XRRGetOutputInfo(QX11Info::display(), m_screen->resources(), m_id);
134 if (RandR::timestamp != info->timestamp)
135 RandR::timestamp = info->timestamp;
137 m_possibleCrtcs.clear();
138 for (int i = 0; i < info->ncrtc; ++i)
139 m_possibleCrtcs.append(info->crtcs[i]);
141 //check if the crtc changed
142 if (info->crtc != m_crtc->id())
144 kDebug() << "CRTC changed for " << m_name << ": " << info->crtc;
145 setCrtc(m_screen->crtc(info->crtc));
146 changes |= RandR::ChangeCrtc;
149 bool connected = (info->connection == RR_Connected);
150 if (connected != m_connected)
152 m_connected = connected;
153 changes |= RandR::ChangeConnection;
157 XRRFreeOutputInfo(info);
159 if (changes && notify)
160 emit outputChanged(m_id, changes);
164 void RandROutput::handleEvent(XRROutputChangeNotifyEvent
*event
)
168 kDebug() << "[OUTPUT] Got event for " << m_name
;
169 kDebug() << " crtc: " << event
->crtc
;
170 kDebug() << " mode: " << event
->mode
;
171 kDebug() << " rotation: " << event
->rotation
;
172 kDebug() << " connection: " << event
->connection
;
174 //FIXME: handling these events incorrectly, causing an X11 I/O error...
176 kWarning() << "FIXME: Output event ignored!";
179 RRCrtc currentCrtc
= m_crtc
->id();
180 if (event
->crtc
!= currentCrtc
)
182 changed
|= RandR::ChangeCrtc
;
183 // update crtc settings
184 if (currentCrtc
!= None
)
185 m_crtc
->loadSettings(true);
186 //m_screen->crtc(m_currentCrtc)->loadSettings(true);
187 setCrtc(m_screen
->crtc(event
->crtc
));
188 if (currentCrtc
!= None
)
189 m_crtc
->loadSettings(true);
192 if (event
->mode
!= mode().id())
193 changed
|= RandR::ChangeMode
;
195 if (event
->rotation
!= rotation())
196 changed
|= RandR::ChangeRotation
;
198 if((event
->connection
== RR_Connected
) != m_connected
)
200 changed
|= RandR::ChangeConnection
;
201 m_connected
= (event
->connection
== RR_Connected
);
202 if (!m_connected
&& currentCrtc
!= None
)
206 // check if we are still connected, if not, release the crtc connection
207 if(!m_connected
&& m_crtc
->isValid())
211 emit
outputChanged(m_id
, changed
);
214 void RandROutput::handlePropertyEvent(XRROutputPropertyNotifyEvent
*event
)
216 // TODO: Do something with this!
217 // By perusing thru some XOrg drivers, some of the properties that can
218 // are configured through XRANDR are:
220 // - TV output formats
222 char *name
= XGetAtomName(QX11Info::display(), event
->property
);
223 kDebug() << "Got XRROutputPropertyNotifyEvent for property Atom " << name
;
227 QString
RandROutput::name() const
232 QString
RandROutput::icon() const
234 // http://www.thinkwiki.org/wiki/Xorg_RandR_1.2#Output_port_names has a
235 // list of possible output names, at least for the intel and radeon drivers
236 // that support RandR 1.2. (nVidia drivers are not yet there at the time
237 // of writing, 2008.)
238 // FIXME: It would also be interesting to be able to get the monitor name
239 // using EDID or something like that, just don't know if it is even possible.
240 if (m_name
.contains("VGA") || m_name
.contains("DVI") || m_name
.contains("TMDS"))
241 return "video-display";
242 else if (m_name
.contains("LVDS"))
243 return "video-display";
244 else if (m_name
.contains("TV") || m_name
.contains("S-video"))
245 return "video-television";
247 return "video-display";
250 CrtcList
RandROutput::possibleCrtcs() const
252 return m_possibleCrtcs
;
255 RandRCrtc
*RandROutput::crtc() const
260 ModeList
RandROutput::modes() const
265 RandRMode
RandROutput::mode() const
273 return m_crtc
->mode();
276 RandRMode
RandROutput::preferredMode(void) const
278 return m_preferredMode
;
281 SizeList
RandROutput::sizes() const
285 foreach(RRMode m
, m_modes
)
287 RandRMode mode
= m_screen
->mode(m
);
290 if (sizeList
.indexOf(mode
.size()) == -1)
291 sizeList
.append(mode
.size());
296 QRect
RandROutput::rect() const
298 if (!m_crtc
->isValid())
299 return QRect(0, 0, 0, 0);
301 return m_crtc
->rect();
304 RateList
RandROutput::refreshRates(const QSize
&s
) const
309 size
= rect().size();
311 foreach(RRMode m
, m_modes
)
313 RandRMode mode
= m_screen
->mode(m
);
316 if (mode
.size() == size
)
317 list
.append(mode
.refreshRate());
322 float RandROutput::refreshRate() const
324 return m_crtc
->mode().refreshRate();
327 int RandROutput::rotations() const
332 int RandROutput::rotation() const
335 return RandR::Rotate0
;
338 return m_crtc
->rotation();
341 bool RandROutput::isConnected() const
346 bool RandROutput::isActive() const
348 return (m_connected
&& m_crtc
->id() != None
);
351 void RandROutput::proposeOriginal()
353 m_proposedRect
= m_originalRect
;
354 m_proposedRate
= m_originalRate
;
355 m_proposedRotation
= m_originalRotation
;
357 if (m_crtc
->id() != None
)
358 m_crtc
->proposeOriginal();
361 void RandROutput::load(KConfig
&config
)
366 KConfigGroup cg
= config
.group("Screen_" + QString::number(m_screen
->index()) +
367 "_Output_" + m_name
);
369 bool active
= cg
.readEntry("Active", true);
371 if (!active
&& !m_screen
->outputsUnified())
373 setCrtc(m_screen
->crtc(None
));
377 // use the current crtc if any, or try to find an empty one
378 if (!m_crtc
->isValid() && m_originalRect
.isValid()) {
379 kDebug() << "Finding empty CRTC for" << m_name
;
380 kDebug() << " with rect = " << m_originalRect
;
382 m_crtc
= findEmptyCrtc();
384 // if there is no crtc we can use, stop processing
385 if (!m_crtc
->isValid())
390 // if the outputs are unified, the screen will handle size changing
391 if (!m_screen
->outputsUnified() || m_screen
->connectedCount() <= 1)
393 m_proposedRect
= (cg
.readEntry("Rect", "0,0,0,0") == "0,0,0,0")
394 ? QRect() // "0,0,0,0" (serialization for QRect()) does not convert to a QRect
395 : cg
.readEntry("Rect", QRect());
396 m_proposedRotation
= cg
.readEntry("Rotation", (int) RandR::Rotate0
);
398 m_proposedRate
= cg
.readEntry("RefreshRate", 0);
401 void RandROutput::save(KConfig
&config
)
403 KConfigGroup cg
= config
.group("Screen_" + QString::number(m_screen
->index()) +
404 "_Output_" + m_name
);
408 if (m_crtc
->id() == None
)
410 cg
.writeEntry("Active", false);
414 cg
.writeEntry("Active", true);
416 // if the outputs are unified, do not save size and rotation
417 // this allow us to set back the size and rotation being used
418 // when the outputs are not unified.
419 if (!m_screen
->outputsUnified() || m_screen
->connectedCount() <=1)
421 cg
.writeEntry("Rect", m_crtc
->rect());
422 cg
.writeEntry("Rotation", m_crtc
->rotation());
424 cg
.writeEntry("RefreshRate", (double)m_crtc
->refreshRate());
427 void RandROutput::proposeRefreshRate(float rate
)
429 m_originalRate
= refreshRate();
430 m_proposedRate
= rate
;
433 void RandROutput::proposeRect(const QRect
&r
)
435 m_originalRect
= rect();
439 void RandROutput::proposeRotation(int r
)
441 m_originalRotation
= rotation();
442 m_proposedRotation
= r
;
445 void RandROutput::slotChangeSize(QAction
*action
)
447 QSize size
= action
->data().toSize();
448 m_proposedRect
.setSize(size
);
449 applyProposed(RandR::ChangeRect
, true);
452 void RandROutput::slotChangeRotation(QAction
*action
)
454 m_proposedRotation
= action
->data().toInt();
455 applyProposed(RandR::ChangeRotation
, true);
458 void RandROutput::slotChangeRefreshRate(QAction
*action
)
460 float rate
= action
->data().toDouble();
462 m_proposedRate
= rate
;
463 applyProposed(RandR::ChangeRate
, true);
466 void RandROutput::slotDisable()
468 proposeRect(QRect());
469 proposeRefreshRate(0);
470 setCrtc(m_screen
->crtc(None
));
473 void RandROutput::slotEnable()
478 kDebug() << "Attempting to enable" << m_name
;
479 RandRCrtc
*crtc
= findEmptyCrtc();
485 RandRCrtc
*RandROutput::findEmptyCrtc()
489 foreach(RRCrtc c
, m_possibleCrtcs
)
491 crtc
= m_screen
->crtc(c
);
492 if (crtc
->connectedOutputs().count() == 0)
499 bool RandROutput::tryCrtc(RandRCrtc
*crtc
, int changes
)
501 kDebug() << "Trying to change output" << m_name
<< "to CRTC" << crtc
->id() << "...";
502 RandRCrtc
*oldCrtc
= m_crtc
;
504 // if we are not yet using this crtc, switch to use it
505 if (crtc
->id() != oldCrtc
->id())
510 if (changes
& RandR::ChangeRect
)
512 crtc
->proposeSize(m_proposedRect
.size());
513 crtc
->proposePosition(m_proposedRect
.topLeft());
515 if (changes
& RandR::ChangeRotation
)
516 crtc
->proposeRotation(m_proposedRotation
);
517 if (changes
& RandR::ChangeRate
)
518 crtc
->proposeRefreshRate(m_proposedRate
);
520 if (crtc
->applyProposed()) {
521 kDebug() << "Changed output" << m_name
<< "to CRTC" << crtc
->id();
522 kDebug() << " ( from old CRTC" << oldCrtc
->id() << ")";
526 // revert changes if we didn't succeed
527 crtc
->proposeOriginal();
528 crtc
->applyProposed();
530 // switch back to the old crtc
531 kDebug() << "Failed to change output" << m_name
<< "to CRTC" << crtc
->id();
532 kDebug() << " Switching back to old CRTC" << oldCrtc
->id();
537 bool RandROutput::applyProposed(int changes
, bool confirm
)
539 // Don't try to disable an already disabled output.
540 if (!m_proposedRect
.isValid() && !m_crtc
->isValid()) {
543 // Don't try to change an enabled output if there is nothing to change.
544 if (m_crtc
->isValid()
545 && (m_crtc
->rect() == m_proposedRect
|| !(changes
& RandR::ChangeRect
))
546 && (m_crtc
->rotation() == m_proposedRotation
|| !(changes
& RandR::ChangeRotation
))
547 && ((m_crtc
->refreshRate() == m_proposedRate
|| !m_proposedRate
|| !(changes
& RandR::ChangeRate
))))
551 kDebug() << "Applying proposed changes for output" << m_name
<< "...";
553 KConfig
cfg("krandrrc");
556 // first try to apply to the already attached crtc if any
557 if (m_crtc
->isValid())
560 if (tryCrtc(crtc
, changes
))
562 if ( !confirm
|| (confirm
&& RandR::confirm(crtc
->rect())) )
569 crtc
->proposeOriginal();
570 crtc
->applyProposed();
576 //then try an empty crtc
577 crtc
= findEmptyCrtc();
579 // TODO: check if we can add this output to a CRTC which already has an output
584 // try the crtc, and if no confirmation is needed or the user confirm, save the new settings
585 if (tryCrtc(crtc
, changes
))
587 if ( !confirm
|| (confirm
&& RandR::confirm(crtc
->rect())) )
594 crtc
->proposeOriginal();
595 crtc
->applyProposed();
603 bool RandROutput::setCrtc(RandRCrtc
*crtc
, bool applyNow
)
606 if( !crtc
|| (m_crtc
&& crtc
->id() == m_crtc
->id()) )
609 kDebug() << "Setting CRTC" << crtc
->id()
610 << (crtc
->isValid() ? "(enabled)" : "(disabled)")
611 << "on output" << m_name
;
613 if(m_crtc
&& m_crtc
->isValid()) {
614 disconnect(m_crtc
, SIGNAL(crtcChanged(RRCrtc
, int)),
615 this, SLOT(slotCrtcChanged(RRCrtc
, int)));
617 m_crtc
->removeOutput(m_id
);
618 m_crtc
->applyProposed();
621 if (!m_crtc
->isValid())
624 m_crtc
->addOutput(m_id
);
625 connect(m_crtc
, SIGNAL(crtcChanged(RRCrtc
, int)),
626 this, SLOT(slotCrtcChanged(RRCrtc
, int)));
631 void RandROutput::slotCrtcChanged(RRCrtc c
, int changes
)
635 //FIXME select which changes we should notify
636 emit
outputChanged(m_id
, changes
);
639 #include "randroutput.moc"