not quite so much needs to be delayed to the init() function
[personal-kdebase.git] / workspace / kcontrol / randr / randroutput.cpp
blobb91cf699df6672ec4396a45ff492cca51336c20b
1 /*
2 * Copyright (c) 2007 Gustavo Pichorim Boiko <gustavo.boiko@kdemail.net>
3 * Copyright (c) 2007, 2008 Harry Bock <hbock@providence.edu>
4 *
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"
25 #include <KConfig>
26 #include <KConfigGroup>
27 #include <QX11Info>
28 #include <QAction>
30 RandROutput::RandROutput(RandRScreen *parent, RROutput id)
31 : QObject(parent)
33 m_screen = parent;
34 Q_ASSERT(m_screen);
36 m_id = id;
37 m_crtc = 0;
38 m_rotations = 0;
40 queryOutputInfo();
42 m_proposedRotation = m_originalRotation;
43 m_proposedRate = m_originalRate;
44 m_proposedRect = m_originalRect;
47 RandROutput::~RandROutput()
51 RROutput RandROutput::id() const
53 return m_id;
56 RandRScreen *RandROutput::screen() const
58 return m_screen;
61 void RandROutput::queryOutputInfo(void)
63 XRROutputInfo *info = XRRGetOutputInfo(QX11Info::display(), m_screen->resources(), m_id);
64 Q_ASSERT(info);
66 if (RandR::timestamp != info->timestamp)
67 RandR::timestamp = info->timestamp;
69 // Set up the output's connection status, name, and current
70 // CRT controller.
71 m_connected = (info->connection == RR_Connected);
72 m_name = info->name;
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 << ":";
80 if (!info->ncrtc) {
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?
89 m_modes.clear();
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
99 m_rotations = 0;
100 for (int i = 0; i < m_possibleCrtcs.count(); ++i)
102 RandRCrtc *crtc = m_screen->crtc(m_possibleCrtcs.at(i));
103 Q_ASSERT(crtc);
104 m_rotations |= crtc->rotations();
106 m_originalRotation = m_crtc->rotation();
107 m_originalRate = m_crtc->refreshRate();
108 m_originalRect = m_crtc->rect();
110 if(isConnected()) {
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)
122 Q_UNUSED(notify);
123 queryOutputInfo();
125 kDebug() << "STUB: calling queryOutputInfo instead. Check if this has "
126 "any undesired effects. ";
129 int changes = 0;
131 XRROutputInfo *info = XRRGetOutputInfo(QX11Info::display(), m_screen->resources(), m_id);
132 Q_ASSERT(info);
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;
156 // free the info
157 XRRFreeOutputInfo(info);
159 if (changes && notify)
160 emit outputChanged(m_id, changes);
164 void RandROutput::handleEvent(XRROutputChangeNotifyEvent *event)
166 int changed = 0;
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...
175 // Disable for now.
176 kWarning() << "FIXME: Output event ignored!";
177 return;
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)
203 setCrtc(None);
206 // check if we are still connected, if not, release the crtc connection
207 if(!m_connected && m_crtc->isValid())
208 setCrtc(None);
210 if(changed)
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:
219 // - LVDS Backlights
220 // - TV output formats
222 char *name = XGetAtomName(QX11Info::display(), event->property);
223 kDebug() << "Got XRROutputPropertyNotifyEvent for property Atom " << name;
224 XFree(name);
227 QString RandROutput::name() const
229 return m_name;
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
257 return m_crtc;
260 ModeList RandROutput::modes() const
262 return m_modes;
265 RandRMode RandROutput::mode() const
267 if (!isConnected())
268 return None;
270 if (!m_crtc)
271 return RandRMode();
273 return m_crtc->mode();
276 RandRMode RandROutput::preferredMode(void) const
278 return m_preferredMode;
281 SizeList RandROutput::sizes() const
283 SizeList sizeList;
285 foreach(RRMode m, m_modes)
287 RandRMode mode = m_screen->mode(m);
288 if (!mode.isValid())
289 continue;
290 if (sizeList.indexOf(mode.size()) == -1)
291 sizeList.append(mode.size());
293 return sizeList;
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
306 RateList list;
307 QSize size = s;
308 if (!size.isValid())
309 size = rect().size();
311 foreach(RRMode m, m_modes)
313 RandRMode mode = m_screen->mode(m);
314 if (!mode.isValid())
315 continue;
316 if (mode.size() == size)
317 list.append(mode.refreshRate());
319 return list;
322 float RandROutput::refreshRate() const
324 return m_crtc->mode().refreshRate();
327 int RandROutput::rotations() const
329 return m_rotations;
332 int RandROutput::rotation() const
334 if (!isActive())
335 return RandR::Rotate0;
337 Q_ASSERT(m_crtc);
338 return m_crtc->rotation();
341 bool RandROutput::isConnected() const
343 return m_connected;
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)
363 if (!m_connected)
364 return;
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));
374 return;
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())
386 return;
388 setCrtc(m_crtc);
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);
405 if (!m_connected)
406 return;
408 if (m_crtc->id() == None)
410 cg.writeEntry("Active", false);
411 return;
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();
436 m_proposedRect = r;
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()
475 if(!m_connected)
476 return;
478 kDebug() << "Attempting to enable" << m_name;
479 RandRCrtc *crtc = findEmptyCrtc();
481 if(crtc)
482 setCrtc(crtc);
485 RandRCrtc *RandROutput::findEmptyCrtc()
487 RandRCrtc *crtc = 0;
489 foreach(RRCrtc c, m_possibleCrtcs)
491 crtc = m_screen->crtc(c);
492 if (crtc->connectedOutputs().count() == 0)
493 return crtc;
496 return 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())
506 setCrtc(crtc);
508 crtc->setOriginal();
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() << ")";
523 return true;
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();
533 setCrtc(oldCrtc);
534 return false;
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()) {
541 return true;
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))))
549 return true;
551 kDebug() << "Applying proposed changes for output" << m_name << "...";
553 KConfig cfg("krandrrc");
554 RandRCrtc *crtc;
556 // first try to apply to the already attached crtc if any
557 if (m_crtc->isValid())
559 crtc = m_crtc;
560 if (tryCrtc(crtc, changes))
562 if ( !confirm || (confirm && RandR::confirm(crtc->rect())) )
564 save(cfg);
565 return true;
567 else
569 crtc->proposeOriginal();
570 crtc->applyProposed();
573 return false;
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
580 // connection
581 if (!crtc)
582 return false;
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())) )
589 save(cfg);
590 return true;
592 else
594 crtc->proposeOriginal();
595 crtc->applyProposed();
596 return false;
600 return false;
603 bool RandROutput::setCrtc(RandRCrtc *crtc, bool applyNow)
605 Q_UNUSED(applyNow);
606 if( !crtc || (m_crtc && crtc->id() == m_crtc->id()) )
607 return false;
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();
620 m_crtc = crtc;
621 if (!m_crtc->isValid())
622 return true;
624 m_crtc->addOutput(m_id);
625 connect(m_crtc, SIGNAL(crtcChanged(RRCrtc, int)),
626 this, SLOT(slotCrtcChanged(RRCrtc, int)));
628 return true;
631 void RandROutput::slotCrtcChanged(RRCrtc c, int changes)
633 Q_UNUSED(c);
635 //FIXME select which changes we should notify
636 emit outputChanged(m_id, changes);
639 #include "randroutput.moc"