20 #include "lowlevel_randr.h"
27 #include <kapplication.h>
28 #include <kiconloader.h>
29 #include <dcopclient.h>
31 #include <kactivelabel.h>
33 #include "ktimerdialog.h"
37 #define INT32 _X11INT32
38 #include <X11/Xproto.h>
41 #include <X11/extensions/Xrandr.h>
43 class RandRScreenPrivate
46 RandRScreenPrivate() : config(0L) {};
50 XRRFreeScreenConfigInfo(config);
53 XRRScreenConfiguration* config;
56 RandRScreen::RandRScreen(
int screenIndex)
57 : d(new RandRScreenPrivate())
58 , m_screen(screenIndex)
65 RandRScreen::~RandRScreen()
70 void RandRScreen::loadSettings()
73 XRRFreeScreenConfigInfo(d->config);
75 d->config = XRRGetScreenInfo(qt_xdisplay(), RootWindow(qt_xdisplay(), m_screen));
79 m_currentSize = m_proposedSize = XRRConfigCurrentConfiguration(d->config, &rotation);
80 m_currentRotation = m_proposedRotation = rotation;
83 m_currentSize = m_proposedSize = 0;
84 m_currentRotation = m_proposedRotation = 0;
92 XRRScreenSize* sizes = XRRSizes(qt_xdisplay(), m_screen, &numSizes);
93 for (
int i = 0; i < numSizes; i++) {
94 m_pixelSizes.append(TQSize(sizes[i].width, sizes[i].height));
95 m_mmSizes.append(TQSize(sizes[i].mwidth, sizes[i].mheight));
98 m_rotations = XRRRotations(qt_xdisplay(), m_screen, &rotation);
102 ScreenInfo *screeninfo = internal_read_screen_info(qt_xdisplay());
103 XRROutputInfo *output_info = screeninfo->outputs[m_screen]->info;
104 CrtcInfo *current_crtc = screeninfo->outputs[m_screen]->cur_crtc;
105 int numSizes = screeninfo->res->nmode;
106 for (
int i = 0; i < numSizes; i++) {
107 TQSize newSize = TQSize(screeninfo->res->modes[i].width, screeninfo->res->modes[i].height);
108 if (!m_pixelSizes.contains(newSize)) {
109 m_pixelSizes.append(newSize);
110 m_mmSizes.append(TQSize(output_info->mm_width, output_info->mm_height));
114 m_rotations = current_crtc->rotations;
115 m_currentRotation = m_proposedRotation = current_crtc->cur_rotation;
120 m_currentRefreshRate = m_proposedRefreshRate = refreshRateHzToIndex(m_currentSize, XRRConfigCurrentRate(d->config));
123 m_currentRefreshRate = m_proposedRefreshRate = 0;
127 void RandRScreen::setOriginal()
129 m_originalSize = m_currentSize;
130 m_originalRotation = m_currentRotation;
131 m_originalRefreshRate = m_currentRefreshRate;
134 bool RandRScreen::applyProposed()
141 d->config = XRRGetScreenInfo(qt_xdisplay(), RootWindow(qt_xdisplay(), m_screen));
146 if (proposedRefreshRate() < 0)
147 status = XRRSetScreenConfig(qt_xdisplay(), d->config, DefaultRootWindow(qt_xdisplay()), (SizeID)proposedSize(), (Rotation)proposedRotation(), CurrentTime);
149 if( refreshRateIndexToHz(proposedSize(), proposedRefreshRate()) <= 0 ) {
150 m_proposedRefreshRate = 0;
152 status = XRRSetScreenConfigAndRate(qt_xdisplay(), d->config, DefaultRootWindow(qt_xdisplay()), (SizeID)proposedSize(), (Rotation)proposedRotation(), refreshRateIndexToHz(proposedSize(), proposedRefreshRate()), CurrentTime);
158 ScreenInfo *screeninfo = internal_read_screen_info(qt_xdisplay());
159 screeninfo->cur_width = (*m_pixelSizes.at(proposedSize())).width();
160 screeninfo->cur_height = (*m_pixelSizes.at(proposedSize())).height();
161 internal_main_low_apply(screeninfo);
163 status = RRSetConfigSuccess;
168 if (status == RRSetConfigSuccess) {
169 m_currentSize = m_proposedSize;
170 m_currentRotation = m_proposedRotation;
171 m_currentRefreshRate = m_proposedRefreshRate;
178 bool RandRScreen::applyProposedAndConfirm()
180 if (proposedChanged()) {
183 if (applyProposed()) {
197 bool RandRScreen::confirm()
204 KTimerDialog acceptDialog ( 15000, KTimerDialog::CountDown,
208 i18n(
"Confirm Display Setting Change"),
209 KTimerDialog::Ok|KTimerDialog::Cancel,
210 KTimerDialog::Cancel);
212 acceptDialog.setButtonOK(KGuiItem(i18n(
"&Accept Configuration"),
"button_ok"));
213 acceptDialog.setButtonCancel(KGuiItem(i18n(
"&Return to Previous Configuration"),
"button_cancel"));
215 KActiveLabel *
label =
new KActiveLabel(i18n(
"Your screen orientation, size and refresh rate "
216 "have been changed to the requested settings. Please indicate whether you wish to "
217 "keep this configuration. In 15 seconds the display will revert to your previous "
218 "settings."), &acceptDialog,
"userSpecifiedLabel");
220 acceptDialog.setMainWidget(label);
222 KDialog::centerOnScreen(&acceptDialog, m_screen);
224 m_shownDialog = &acceptDialog;
225 connect( m_shownDialog, TQT_SIGNAL( destroyed()),
this, TQT_SLOT( shownDialogDestroyed()));
226 connect( kapp->desktop(), TQT_SIGNAL( resized(
int)),
this, TQT_SLOT( desktopResized()));
228 return acceptDialog.exec();
231 void RandRScreen::shownDialogDestroyed()
233 m_shownDialog = NULL;
234 disconnect( kapp->desktop(), TQT_SIGNAL( resized(
int)),
this, TQT_SLOT( desktopResized()));
237 void RandRScreen::desktopResized()
239 if( m_shownDialog != NULL )
240 KDialog::centerOnScreen(m_shownDialog, m_screen);
243 TQString RandRScreen::changedMessage()
const
245 if (currentRefreshRate() == -1)
246 return i18n(
"New configuration:\nResolution: %1 x %2\nOrientation: %3")
247 .arg(currentPixelWidth())
248 .arg(currentPixelHeight())
249 .arg(currentRotationDescription());
251 return i18n(
"New configuration:\nResolution: %1 x %2\nOrientation: %3\nRefresh rate: %4")
252 .arg(currentPixelWidth())
253 .arg(currentPixelHeight())
254 .arg(currentRotationDescription())
255 .arg(currentRefreshRateDescription());
258 bool RandRScreen::changedFromOriginal()
const
260 return m_currentSize != m_originalSize || m_currentRotation != m_originalRotation || m_currentRefreshRate != m_originalRefreshRate;
263 void RandRScreen::proposeOriginal()
265 m_proposedSize = m_originalSize;
266 m_proposedRotation = m_originalRotation;
267 m_proposedRefreshRate = m_originalRefreshRate;
270 bool RandRScreen::proposedChanged()
const
272 return m_currentSize != m_proposedSize || m_currentRotation != m_proposedRotation || m_currentRefreshRate != m_proposedRefreshRate;
275 TQString RandRScreen::rotationName(
int rotation,
bool pastTense,
bool capitalised)
280 return i18n(
"Normal");
282 return i18n(
"Left (90 degrees)");
284 return i18n(
"Upside-down (180 degrees)");
286 return i18n(
"Right (270 degrees)");
288 return i18n(
"Mirror horizontally");
290 return i18n(
"Mirror vertically");
292 return i18n(
"Unknown orientation");
297 return i18n(
"Normal");
299 return i18n(
"Rotated 90 degrees counterclockwise");
301 return i18n(
"Rotated 180 degrees counterclockwise");
303 return i18n(
"Rotated 270 degrees counterclockwise");
305 if (rotation & RR_Reflect_X)
306 if (rotation & RR_Reflect_Y)
308 return i18n(
"Mirrored horizontally and vertically");
310 return i18n(
"mirrored horizontally and vertically");
313 return i18n(
"Mirrored horizontally");
315 return i18n(
"mirrored horizontally");
316 else if (rotation & RR_Reflect_Y)
318 return i18n(
"Mirrored vertically");
320 return i18n(
"mirrored vertically");
323 return i18n(
"Unknown orientation");
325 return i18n(
"unknown orientation");
329 TQPixmap RandRScreen::rotationIcon(
int rotation)
const
332 if (!(m_currentRotation & RR_Rotate_0) && rotation & (RR_Rotate_0 | RR_Rotate_90 | RR_Rotate_180 | RR_Rotate_270)) {
333 int currentAngle = m_currentRotation & (RR_Rotate_90 | RR_Rotate_180 | RR_Rotate_270);
334 switch (currentAngle) {
347 if (rotation > RR_Rotate_270) {
354 return SmallIcon(
"up");
356 return SmallIcon(
"back");
358 return SmallIcon(
"down");
360 return SmallIcon(
"forward");
364 return SmallIcon(
"stop");
368 TQString RandRScreen::currentRotationDescription()
const
370 TQString ret = rotationName(m_currentRotation & RotateMask);
372 if (m_currentRotation != m_currentRotation & RotateMask)
373 if (m_currentRotation & RR_Rotate_0)
374 ret = rotationName(m_currentRotation & (RR_Reflect_X + RR_Reflect_X),
true,
true);
376 ret +=
", " + rotationName(m_currentRotation & (RR_Reflect_X + RR_Reflect_X),
true,
false);
381 int RandRScreen::rotationIndexToDegree(
int rotation)
const
383 switch (rotation & RotateMask) {
398 int RandRScreen::rotationDegreeToIndex(
int degree)
const
405 return RR_Rotate_180;
408 return RR_Rotate_270;
415 int RandRScreen::currentPixelWidth()
const
417 return m_pixelSizes[m_currentSize].width();
420 int RandRScreen::currentPixelHeight()
const
422 return m_pixelSizes[m_currentSize].height();
425 int RandRScreen::currentMMWidth()
const
427 return m_pixelSizes[m_currentSize].width();
430 int RandRScreen::currentMMHeight()
const
432 return m_pixelSizes[m_currentSize].height();
435 TQStringList RandRScreen::refreshRates(
int size)
const
441 short* rates = XRRRates(qt_xdisplay(), m_screen, (SizeID)size, &nrates);
443 for (
int i = 0; i < nrates; i++)
444 ret << refreshRateDirectDescription(rates[i]);
448 ScreenInfo *screeninfo = internal_read_screen_info(qt_xdisplay());
449 int numSizes = screeninfo->res->nmode;
450 for (
int i = 0; i < numSizes; i++) {
451 int refresh_rate = ((screeninfo->res->modes[i].dotClock*1.0)/((screeninfo->res->modes[i].hTotal)*(screeninfo->res->modes[i].vTotal)*1.0));
452 TQString newRate = refreshRateDirectDescription(refresh_rate);
453 if (!ret.contains(newRate)) {
462 TQString RandRScreen::refreshRateDirectDescription(
int rate)
const
464 return i18n(
"Refresh rate in Hertz (Hz)",
"%1 Hz").arg(rate);
467 TQString RandRScreen::refreshRateIndirectDescription(
int size,
int index)
const
469 return i18n(
"Refresh rate in Hertz (Hz)",
"%1 Hz").arg(refreshRateIndexToHz(size, index));
472 TQString RandRScreen::refreshRateDescription(
int size,
int index)
const
474 return refreshRates(size)[index];
477 bool RandRScreen::proposeRefreshRate(
int index)
479 if (index >= 0 && (
int)refreshRates(proposedSize()).count() > index) {
480 m_proposedRefreshRate = index;
487 int RandRScreen::currentRefreshRate()
const
489 return m_currentRefreshRate;
492 TQString RandRScreen::currentRefreshRateDescription()
const
494 return refreshRateIndirectDescription(m_currentSize, m_currentRefreshRate);
497 int RandRScreen::proposedRefreshRate()
const
499 return m_proposedRefreshRate;
502 int RandRScreen::refreshRateHzToIndex(
int size,
int hz)
const
505 short* rates = XRRRates(qt_xdisplay(), m_screen, (SizeID)size, &nrates);
507 for (
int i = 0; i < nrates; i++)
518 int RandRScreen::refreshRateIndexToHz(
int size,
int index)
const
521 short* rates = XRRRates(qt_xdisplay(), m_screen, (SizeID)size, &nrates);
523 if (nrates == 0 || index < 0)
533 int RandRScreen::numSizes()
const
535 return m_pixelSizes.count();
538 const TQSize& RandRScreen::pixelSize(
int index)
const
540 return m_pixelSizes[index];
543 const TQSize& RandRScreen::mmSize(
int index)
const
545 return m_mmSizes[index];
548 int RandRScreen::sizeIndex(TQSize pixelSize)
const
550 for (uint i = 0; i < m_pixelSizes.count(); i++)
551 if (m_pixelSizes[i] == pixelSize)
557 int RandRScreen::rotations()
const
562 int RandRScreen::currentRotation()
const
564 return m_currentRotation;
567 int RandRScreen::currentSize()
const
569 return m_currentSize;
572 int RandRScreen::proposedRotation()
const
574 return m_proposedRotation;
577 void RandRScreen::proposeRotation(
int newRotation)
579 m_proposedRotation = newRotation & OrientationMask;
582 int RandRScreen::proposedSize()
const
584 return m_proposedSize;
587 bool RandRScreen::proposeSize(
int newSize)
589 if ((
int)m_pixelSizes.count() > newSize) {
590 m_proposedSize = newSize;
597 void RandRScreen::load(
KConfig& config)
599 config.
setGroup(TQString(
"Screen%1").arg(m_screen));
601 if (proposeSize(sizeIndex(TQSize(config.
readNumEntry(
"width", currentPixelWidth()), config.
readNumEntry(
"height", currentPixelHeight())))))
602 proposeRefreshRate(refreshRateHzToIndex(proposedSize(), config.
readNumEntry(
"refresh", currentRefreshRate())));
607 void RandRScreen::save(
KConfig& config)
const
609 config.
setGroup(TQString(
"Screen%1").arg(m_screen));
610 config.
writeEntry(
"width", currentPixelWidth());
611 config.
writeEntry(
"height", currentPixelHeight());
612 config.
writeEntry(
"refresh", refreshRateIndexToHz(currentSize(), currentRefreshRate()));
613 config.
writeEntry(
"rotation", rotationIndexToDegree(currentRotation()));
614 config.
writeEntry(
"reflectX", (
bool)(currentRotation() & ReflectMask) == ReflectX);
615 config.
writeEntry(
"reflectY", (
bool)(currentRotation() & ReflectMask) == ReflectY);
618 RandRDisplay::RandRDisplay()
622 Status s = XRRQueryExtension(qt_xdisplay(), &m_eventBase, &m_errorBase);
624 m_errorCode = TQString(
"%1, base %1").arg(s).arg(m_errorBase);
631 Display *randr_display = XOpenDisplay(NULL);
635 screen_num = DefaultScreen (randr_display);
636 root_window = RootWindow (randr_display, screen_num);
637 if (XRRGetScreenResources (randr_display, root_window) == NULL) {
638 m_errorCode = i18n(
"No screens detected");
643 int major_version, minor_version;
644 XRRQueryVersion(qt_xdisplay(), &major_version, &minor_version);
646 m_version = TQString(
"X Resize and Rotate extension version %1.%1").arg(major_version).arg(minor_version);
648 m_numScreens = ScreenCount(qt_xdisplay());
653 m_screens.setAutoDelete(
true);
654 for (
int i = 0; i < m_numScreens; i++) {
655 m_screens.append(
new RandRScreen(i));
658 setCurrentScreen(TQApplication::desktop()->primaryScreen());
661 bool RandRDisplay::isValid()
const
666 const TQString& RandRDisplay::errorCode()
const
671 int RandRDisplay::eventBase()
const
676 int RandRDisplay::screenChangeNotifyEvent()
const
678 return m_eventBase + RRScreenChangeNotify;
681 int RandRDisplay::errorBase()
const
686 const TQString& RandRDisplay::version()
const
691 void RandRDisplay::setCurrentScreen(
int index)
693 m_currentScreenIndex = index;
694 m_currentScreen = m_screens.at(m_currentScreenIndex);
695 Q_ASSERT(m_currentScreen);
698 int RandRDisplay::screenIndexOfWidget(TQWidget* widget)
700 int ret = TQApplication::desktop()->screenNumber(widget);
701 return ret != -1 ? ret : TQApplication::desktop()->primaryScreen();
704 int RandRDisplay::currentScreenIndex()
const
706 return m_currentScreenIndex;
709 void RandRDisplay::refresh()
711 for (RandRScreen* s = m_screens.first(); s; s = m_screens.next())
715 int RandRDisplay::numScreens()
const
720 RandRScreen* RandRDisplay::screen(
int index)
722 return m_screens.at(index);
725 RandRScreen* RandRDisplay::currentScreen()
727 return m_currentScreen;
730 bool RandRDisplay::loadDisplay(
KConfig& config,
bool loadScreens)
733 for (RandRScreen* s = m_screens.first(); s; s = m_screens.next())
736 return applyOnStartup(config);
739 bool RandRDisplay::applyOnStartup(
KConfig& config)
745 bool RandRDisplay::syncTrayApp(
KConfig& config)
751 void RandRDisplay::saveDisplay(
KConfig& config,
bool applyOnStartup,
bool syncTrayApp)
756 config.
writeEntry(
"ApplyOnStartup", applyOnStartup);
757 config.
writeEntry(
"SyncTrayApp", syncTrayApp);
759 for (RandRScreen* s = m_screens.first(); s; s = m_screens.next())
763 void RandRDisplay::applyProposed(
bool confirm)
765 for (
int screenIndex = 0; screenIndex < numScreens(); screenIndex++) {
766 if (screen(screenIndex)->proposedChanged()) {
768 screen(screenIndex)->applyProposedAndConfirm();
770 screen(screenIndex)->applyProposed();
775 bool RandRDisplay::showTestConfigurationDialog()
777 return screen(0)->showTestConfigurationDialog();
780 bool RandRScreen::showTestConfigurationDialog()
787 KTimerDialog acceptDialog ( 15000, KTimerDialog::CountDown,
791 i18n(
"Confirm Display Settings"),
792 KTimerDialog::Ok|KTimerDialog::Cancel,
793 KTimerDialog::Cancel);
795 acceptDialog.setButtonOK(KGuiItem(i18n(
"&Accept Configuration"),
"button_ok"));
796 acceptDialog.setButtonCancel(KGuiItem(i18n(
"&Return to Previous Configuration"),
"button_cancel"));
798 KActiveLabel *label =
new KActiveLabel(i18n(
"Your display devices has been configured "
799 "to match the settings shown above. Please indicate whether you wish to "
800 "keep this configuration. In 15 seconds the display will revert to your previous "
801 "settings."), &acceptDialog,
"userSpecifiedLabel");
803 acceptDialog.setMainWidget(label);
805 KDialog::centerOnScreen(&acceptDialog, 0);
807 m_shownDialog = &acceptDialog;
808 connect( m_shownDialog, TQT_SIGNAL( destroyed()),
this, TQT_SLOT( shownDialogDestroyed()));
809 connect( kapp->desktop(), TQT_SIGNAL( resized(
int)),
this, TQT_SLOT( desktopResized()));
811 return acceptDialog.exec();
814 int RandRScreen::pixelCount(
int index )
const
816 TQSize sz = pixelSize(index);
817 return sz.width() * sz.height();