not quite so much needs to be delayed to the init() function
[personal-kdebase.git] / workspace / kcontrol / randr / randrscreen.cpp
blob40540e2dbcafbe85b8cedd770b60fa6a007afefc
1 /*
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"
24 #include <KConfig>
25 #include <KConfigGroup>
26 #include <QAction>
28 RandRScreen::RandRScreen(int screenIndex)
29 : m_resources(0L)
31 m_index = screenIndex;
32 m_rect = QRect(0, 0, XDisplayWidth(QX11Info::display(), m_index),
33 XDisplayHeight(QX11Info::display(), m_index));
35 m_connectedCount = 0;
36 m_activeCount = 0;
38 loadSettings();
39 load();
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()
53 if (m_resources)
54 XRRFreeScreenResources(m_resources);
56 //qDeleteAll(m_crtcs);
57 //qDeleteAll(m_outputs);
58 //qDeleteAll(m_modes);
61 int RandRScreen::index() const
63 return m_index;
66 XRRScreenResources* RandRScreen::resources() const
68 return m_resources;
71 Window RandRScreen::rootWindow() const
73 return RootWindow(QX11Info::display(), m_index);
76 void RandRScreen::loadSettings(bool notify)
78 bool changed = false;
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
84 Q_UNUSED(status);
85 QSize minSize = QSize(minW, minH);
86 QSize maxSize = QSize(maxW, maxH);
88 if (minSize != m_minSize || maxSize != m_maxSize)
90 m_minSize = minSize;
91 m_maxSize = maxSize;
92 changed = true;
95 if (m_resources)
96 XRRFreeScreenResources(m_resources);
98 m_resources = XRRGetScreenResources(QX11Info::display(), rootWindow());
99 Q_ASSERT(m_resources);
101 RandR::timestamp = m_resources->timestamp;
103 // get all modes
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]);
109 changed = true;
113 //get all crtcs
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);
122 else
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;
130 changed = true;
134 //get all outputs
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);
139 else
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())
147 m_connectedCount++;
148 if (o->isActive())
149 m_activeCount++;
151 changed = true;
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)
170 RandRCrtc *c;
171 RandROutput *o;
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);
181 Q_ASSERT(c);
182 c->handleEvent(crtcEvent);
183 return;
185 case RRNotify_OutputChange:
186 outputEvent = (XRROutputChangeNotifyEvent*)event;
187 o = output(outputEvent->output);
188 Q_ASSERT(o);
189 o->handleEvent(outputEvent);
190 return;
192 case RRNotify_OutputProperty:
193 propertyEvent = (XRROutputPropertyNotifyEvent*)event;
194 o = output(propertyEvent->output);
195 Q_ASSERT(o);
196 o->handlePropertyEvent(propertyEvent);
197 return;
201 QSize RandRScreen::minSize() const
203 return m_minSize;
206 QSize RandRScreen::maxSize() const
208 return m_maxSize;
211 CrtcMap RandRScreen::crtcs() const
213 return m_crtcs;
216 RandRCrtc* RandRScreen::crtc(RRCrtc id) const
218 if (m_crtcs.contains(id))
219 return m_crtcs[id];
221 return 0;
224 OutputMap RandRScreen::outputs() const
226 return m_outputs;
229 RandROutput* RandRScreen::output(RROutput id) const
231 if (m_outputs.contains(id))
232 return m_outputs[id];
234 return 0;
237 ModeMap RandRScreen::modes() const
239 return m_modes;
242 RandRMode RandRScreen::mode(RRMode id) const
244 if (m_modes.contains(id))
245 return m_modes[id];
247 return RandRMode(0);
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())
262 continue;
263 rect = rect.united(output->rect());
267 // check bounds
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())
274 return false;
275 if (rect.height() > m_maxSize.height())
276 return false;
278 return setSize(rect.size());
281 bool RandRScreen::setSize(const QSize &s)
283 if (s == m_rect.size())
284 return true;
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())
290 return false;
292 int widthMM, heightMM;
293 float dpi;
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);
301 m_rect.setSize(s);
303 return true;
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
332 bool first = true;
333 int rotations = RandR::Rotate0;
335 foreach(RandRCrtc *crtc, m_crtcs)
337 if (!crtc->connectedOutputs().count())
338 continue;
340 if (first)
342 rotations = crtc->rotations();
343 first = false;
345 else
346 rotations &= crtc->rotations();
349 return rotations;
352 SizeList RandRScreen::unifiedSizes() const
354 SizeList sizeList;
355 bool first = true;
357 foreach(RandROutput *output, m_outputs)
359 if (!output->isConnected())
360 continue;
362 if (first)
364 // we start using the list from the first output
365 sizeList = output->sizes();
366 first = false;
368 else
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);
381 return sizeList;
384 QRect RandRScreen::rect() const
386 return m_rect;
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");
424 save(cfg);
427 void RandRScreen::load()
429 KConfig cfg("krandrrc");
430 load(cfg);
433 bool RandRScreen::applyProposed(bool confirm)
435 kDebug() << "Applying proposed changes for screen" << m_index << "...";
437 bool succeed = true;
438 QRect r;
440 foreach(RandROutput *output, m_outputs) {
442 r = output->rect();
443 RandROutput::Relation outputRelation;
444 RandROutput *related = output->relation(&outputRelation);
446 if(!related) {
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(),
454 relativeRect.y()));
455 case RandROutput::RightOf:
456 r.setTopLeft(QPoint(relativeRect.x() + r.x(),
457 relativeRect.y()));
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());
467 break;
469 output->proposeRect(r);
471 if(!output->applyProposed()) {
472 succeed = false;
473 break;
477 foreach(RandROutput *output, m_outputs)
479 r = output->rect();
480 if (!output->applyProposed())
482 succeed = false;
483 break;
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
495 if (succeed)
496 return true;
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();
506 o->applyProposed();
509 return false;
512 void RandRScreen::unifyOutputs()
514 KConfig cfg("krandrrc");
515 SizeList sizes = unifiedSizes();
517 //FIXME: better handle this
518 if (!sizes.count())
519 return;
521 // if there is only one output connected, there is no way to unify it
522 if (m_connectedCount <= 1)
523 return;
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())
535 continue;
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)
541 continue;
543 // this is to get the refresh rate to use
544 //o->load(cfg);
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
551 // do something
552 save();
553 emit configChanged();
556 void RandRScreen::slotResizeUnified(QAction *action)
558 m_unifiedRect.setSize(action->data().toSize());
559 unifyOutputs();
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())
572 output->load(cfg);
573 output->applyProposed();
576 else
578 SizeList sizes = unifiedSizes();
580 if (!sizes.count())
582 // FIXME: this should be better handle
583 return;
586 QSize s = m_unifiedRect.size();
588 // if the last size we used is not available, use the first one
589 // from the list
590 if (sizes.indexOf(s) == -1)
591 s = sizes[0];
593 m_unifiedRect.setTopLeft(QPoint(0,0));
594 m_unifiedRect.setSize(s);
595 unifyOutputs();
599 void RandRScreen::slotRotateUnified(QAction *action)
601 m_unifiedRotation = action->data().toInt();
603 unifyOutputs();
606 void RandRScreen::slotOutputChanged(RROutput id, int changes)
608 Q_UNUSED(id);
609 Q_UNUSED(changes);
611 int connected = 0, active = 0;
612 foreach(RandROutput *output, m_outputs)
614 if (output->isConnected())
615 connected++;
616 if (output->isActive())
617 active++;
620 m_connectedCount = connected;
621 m_activeCount = active;
623 // if there is less than 2 outputs connected, there is no need to unify
624 if (connected <= 1)
625 return;
627 // wait some time before checking the output configuration as some randr
628 // clients do operations in more than one step
629 if(m_outputsUnified)
630 unifyOutputs();
633 #include "randrscreen.moc"