2 * Copyright 2006 Aaron Seigo <aseigo@kde.org>
3 * Copyright 2008 Chani Armitage <chanika@gmail.com>
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as
8 * published by the Free Software Foundation; either version 2,
9 * or (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details
16 * You should have received a copy of the GNU Library General Public
17 * License along with this program; if not, write to the
18 * Free Software Foundation, Inc.,
19 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
22 // plasma.loadEngine("hardware")
24 // plasma.connect(graph, "hardware", "cpu");
26 #include "plasmaapp.h"
30 #ifndef _SC_PHYS_PAGES
32 #include <sys/types.h>
33 #include <sys/sysctl.h>
37 #include <sys/param.h>
38 #include <sys/sysctl.h>
42 #include <QApplication>
43 #include <QDesktopWidget>
44 #include <QPixmapCache>
45 #include <QtDBus/QtDBus>
49 #include <KCmdLineArgs>
50 #include <KWindowSystem>
52 #include <KConfigDialog>
54 //#include <ksmserver_interface.h>
56 #include <plasma/containment.h>
57 #include <plasma/theme.h>
59 #include "appadaptor.h"
60 #include "savercorona.h"
61 #include "saverview.h"
64 #include <X11/extensions/Xrender.h>
66 Atom tag
; //FIXME should this be a member var or what?
67 const unsigned char DIALOG
= 1; //FIXME this is really bad code
68 const unsigned char VIEW
= 2;
71 Colormap colormap
= 0;
76 dpy
= XOpenDisplay(0); // open default display
78 kError() << "Cannot connect to the X server" << endl
;
82 int screen
= DefaultScreen(dpy
);
83 int eventBase
, errorBase
;
85 if (XRenderQueryExtension(dpy
, &eventBase
, &errorBase
)) {
88 templ
.screen
= screen
;
90 templ
.c_class
= TrueColor
;
91 XVisualInfo
*xvi
= XGetVisualInfo(dpy
,
92 VisualScreenMask
| VisualDepthMask
| VisualClassMask
,
94 for (int i
= 0; i
< nvi
; ++i
) {
95 XRenderPictFormat
*format
= XRenderFindVisualFormat(dpy
, xvi
[i
].visual
);
96 if (format
->type
== PictTypeDirect
&& format
->direct
.alphaMask
) {
97 visual
= xvi
[i
].visual
;
98 colormap
= XCreateColormap(dpy
, RootWindow(dpy
, screen
), visual
, AllocNone
);
104 kDebug() << (colormap
? "Plasma has an argb visual" : "Plasma lacks an argb visual") << visual
<< colormap
;
105 kDebug() << ((KWindowSystem::compositingActive() && colormap
) ? "Plasma can use COMPOSITE for effects"
106 : "Plasma is COMPOSITE-less") << "on" << dpy
;
109 PlasmaApp
* PlasmaApp::self()
113 //kDebug() << "new PlasmaApp";
114 return new PlasmaApp(dpy
, visual
? Qt::HANDLE(visual
) : 0, colormap
? Qt::HANDLE(colormap
) : 0);
117 //kDebug() << "existing PlasmaApp";
118 return qobject_cast
<PlasmaApp
*>(kapp
);
121 PlasmaApp::PlasmaApp(Display
* display
, Qt::HANDLE visual
, Qt::HANDLE colormap
)
122 : KUniqueApplication(display
, visual
, colormap
),
127 KGlobal::locale()->insertCatalog("libplasma");
129 new PlasmaAppAdaptor(this);
130 QDBusConnection::sessionBus().registerObject("/App", this);
132 //FIXME this is probably totally invalid
133 // Enlarge application pixmap cache
134 // Calculate the size required to hold background pixmaps for all screens.
135 // Add 10% so that other (smaller) pixmaps can also be cached.
137 QDesktopWidget
*desktop
= QApplication::desktop();
138 for (int i
= 0; i
< desktop
->numScreens(); i
++) {
139 QRect geometry
= desktop
->screenGeometry(i
);
140 cacheSize
+= 4 * geometry
.width() * geometry
.height() / 1024;
142 cacheSize
+= cacheSize
/ 10;
144 // Calculate the size of physical system memory; _SC_PHYS_PAGES *
145 // _SC_PAGESIZE is documented to be able to overflow 32-bit integers,
146 // so apply a 10-bit shift. FreeBSD 6-STABLE doesn't have _SC_PHYS_PAGES
147 // (it is documented in FreeBSD 7-STABLE as "Solaris and Linux extension")
148 // so use sysctl in those cases.
149 #if defined(_SC_PHYS_PAGES)
150 int memorySize
= sysconf(_SC_PHYS_PAGES
);
151 memorySize
*= sysconf(_SC_PAGESIZE
) / 1024;
155 size_t size
= sizeof(sysctlbuf
);
157 // This could actually use hw.physmem instead, but I can't find
158 // reliable documentation on how to read the value (which may
159 // not fit in a 32 bit integer).
160 if (!sysctlbyname("vm.stats.vm.v_page_size", sysctlbuf
, &size
, NULL
, 0)) {
161 memorySize
= sysctlbuf
[0] / 1024;
162 size
= sizeof(sysctlbuf
);
163 if (!sysctlbyname("vm.stats.vm.v_page_count", sysctlbuf
, &size
, NULL
, 0)) {
164 memorySize
*= sysctlbuf
[0];
171 static int mib
[] = { CTL_HW
, HW_PHYSMEM
};
173 len
= sizeof(memorySize
);
174 sysctl(mib
, 2, &memorySize
, &len
, NULL
, 0);
177 // If you have no suitable sysconf() interface and are not FreeBSD,
178 // then you are out of luck and get a compile error.
181 // Increase the pixmap cache size to 1% of system memory if it isn't already
182 // larger so as to maximize cache usage. 1% of 1GB ~= 10MB.
183 if (cacheSize
< memorySize
/ 100) {
184 cacheSize
= memorySize
/ 100;
187 kDebug() << "Setting the pixmap cache size to" << cacheSize
<< "kilobytes";
188 QPixmapCache::setCacheLimit(cacheSize
);
190 KConfigGroup
cg(KGlobal::config(), "General");
191 Plasma::Theme::defaultTheme()->setFont(cg
.readEntry("desktopFont", font()));
192 m_activeOpacity
= cg
.readEntry("activeOpacity", 1.0);
193 m_idleOpacity
= cg
.readEntry("idleOpacity", 1.0);
195 enableCheats(KCmdLineArgs::parsedArgs()->isSet("cheats"));
197 //we have to keep an eye on created windows
198 tag
= XInternAtom(QX11Info::display(), "_KDE_SCREENSAVER_OVERRIDE", False
);
199 qApp
->installEventFilter(this);
201 // this line initializes the corona.
204 connect(QApplication::desktop(), SIGNAL(resized(int)), SLOT(adjustSize(int)));
205 connect(this, SIGNAL(aboutToQuit()), this, SLOT(cleanup()));
208 PlasmaApp::~PlasmaApp()
210 //TODO: This manual sync() should not be necessary. Remove it when
212 KGlobal::config()->sync();
215 void PlasmaApp::cleanup()
218 m_corona
->saveLayout();
225 void PlasmaApp::enableCheats(bool enable
)
230 bool PlasmaApp::cheatsEnabled() const
235 void PlasmaApp::setActiveOpacity(qreal opacity
)
237 if (opacity
== m_activeOpacity
) {
240 m_activeOpacity
= opacity
;
242 //assume it's active, since things are happening
243 m_view
->setWindowOpacity(opacity
);
245 KConfigGroup
cg(KGlobal::config(), "General");
246 cg
.writeEntry("activeOpacity", opacity
); //TODO trigger a save
249 void PlasmaApp::setIdleOpacity(qreal opacity
)
251 if (opacity
== m_idleOpacity
) {
254 m_idleOpacity
= opacity
;
255 KConfigGroup
cg(KGlobal::config(), "General");
256 cg
.writeEntry("idleOpacity", opacity
); //TODO trigger a save
259 qreal
PlasmaApp::activeOpacity() const
261 return m_activeOpacity
;
264 qreal
PlasmaApp::idleOpacity() const
266 return m_idleOpacity
;
270 void PlasmaApp::activate()
273 m_view
->setWindowOpacity(m_activeOpacity
);
279 void PlasmaApp::deactivate()
282 if (m_view
->isVisible()) {
283 if (qFuzzyCompare(m_idleOpacity
, 0.0)) {
287 m_view
->setWindowOpacity(m_idleOpacity
);
295 uint
PlasmaApp::viewWinId()
298 //kDebug() << m_view->winId();
299 return m_view
->effectiveWinId();
304 void PlasmaApp::adjustSize(int screen
)
309 //FIXME someone needs to tell us what size to use if we've got >1 screen
310 QDesktopWidget
*desktop
= QApplication::desktop();
311 QRect geom
= desktop
->screenGeometry(0);
312 m_view
->setGeometry(geom
);
315 Plasma::Corona
* PlasmaApp::corona()
318 SaverCorona
*c
= new SaverCorona(this);
319 connect(c
, SIGNAL(containmentAdded(Plasma::Containment
*)),
320 this, SLOT(createView(Plasma::Containment
*)));
321 kDebug() << "connected to containmentAdded";
323 foreach (DesktopView *view, m_desktops) {
324 connect(c, SIGNAL(screenOwnerChanged(int,int,Plasma::Containment*)),
325 view, SLOT(screenOwnerChanged(int,int,Plasma::Containment*)));
328 c
->setItemIndexMethod(QGraphicsScene::NoIndex
);
329 c
->initializeLayout();
330 kDebug() << "layout should exist";
338 bool PlasmaApp::hasComposite()
340 return colormap
&& KWindowSystem::compositingActive();
343 //I think we need this for when the corona loads the default setup
344 //but maybe something simpler would suffice
345 void PlasmaApp::createView(Plasma::Containment
*containment
)
347 kDebug() << "Containment name:" << containment
->name()
348 << "| type" << containment
->containmentType()
349 << "| screen:" << containment
->screen()
350 << "| geometry:" << containment
->geometry()
351 << "| zValue:" << containment
->zValue();
354 // we already have a view for this screen
358 kDebug() << "creating a view for" << containment
->screen() << "and we have"
359 << QApplication::desktop()->numScreens() << "screens";
361 // we have a new screen. neat.
362 m_view
= new SaverView(containment
, 0);
364 connect(m_corona, SIGNAL(screenOwnerChanged(int,int,Plasma::Containment*)),
365 view, SLOT(screenOwnerChanged(int,int,Plasma::Containment*)));
367 //FIXME is this the right geometry for multi-screen?
368 m_view
->setGeometry(QApplication::desktop()->screenGeometry(containment
->screen()));
371 //TODO quit button...
372 kDebug() << "cheats enabled";
373 KAction
*showAction
= new KAction(this);
374 showAction
->setText(i18n("Show plasma-overlay"));
375 showAction
->setObjectName("Show plasma-overlay"); // NO I18N
376 showAction
->setGlobalShortcut(KShortcut(Qt::CTRL
+ Qt::Key_F11
));
377 connect(showAction
, SIGNAL(triggered()), m_view
, SLOT(showView()));
380 //FIXME why do I get BadWindow?
381 //unsigned char data = VIEW;
382 //XChangeProperty(QX11Info::display(), m_view->effectiveWinId(), tag, tag, 8, PropModeReplace, &data, 1);
384 connect(containment
, SIGNAL(locked()), SLOT(hideDialogs()));
385 connect(containment
, SIGNAL(unlocked()), SLOT(showDialogs()));
387 connect(containment
, SIGNAL(delegateConfigurationInterface(KConfigDialog
*)),
388 SLOT(createConfigurationInterface(KConfigDialog
*)));
390 connect(m_view
, SIGNAL(hidden()), SLOT(lock()));
391 connect(m_view
, SIGNAL(hidden()), SIGNAL(hidden()));
392 if (m_idleOpacity
> 0) {
393 m_view
->setWindowOpacity(m_idleOpacity
);
396 emit
viewCreated(m_view
->effectiveWinId());
399 bool PlasmaApp::eventFilter(QObject
*obj
, QEvent
*event
)
401 if (event
->type() == QEvent::Show
) {
402 //apparently this means we created a new window
403 //so, add a tag to prove it's our window
404 //FIXME using the show event means we tag on every show, not just the first.
405 //harmless but kinda wasteful.
406 QWidget
*widget
= qobject_cast
<QWidget
*>(obj
);
407 if (widget
&& widget
->isWindow() && !(qobject_cast
<QDesktopWidget
*>(widget
) ||
408 /*qobject_cast<SaverView*>(widget) ||*/
409 widget
->testAttribute(Qt::WA_DontShowOnScreen
))) {
410 unsigned char data
= 0;
411 //now we're *really* fucking with things
412 //we force-disable window management and frames to cut off access to wm-y stuff
413 //and to make it easy to check the tag (frames are a pain)
414 Qt::WindowFlags oldFlags
= widget
->windowFlags();
415 Qt::WindowFlags newFlags
= oldFlags
| Qt::X11BypassWindowManagerHint
;
416 if (oldFlags
!= newFlags
) {
417 kDebug() << "!!!!!!!setting flags on!!!!!" << widget
;
418 m_dialogs
.append(widget
);
419 connect(widget
, SIGNAL(destroyed(QObject
*)), SLOT(dialogDestroyed(QObject
*)));
420 widget
->setWindowFlags(newFlags
);
421 widget
->show(); //setting the flags hid it :(
422 //qApp->setActiveWindow(widget); //gives kbd but not mouse events
423 //kDebug() << "parent" << widget->parentWidget();
424 //FIXME why can I only activate these dialogs from this exact line?
425 widget
->activateWindow(); //gives keyboard focus
426 return false; //we'll be back when we get the new show event
427 } else if (qobject_cast
<SaverView
*>(widget
)) {
429 } else if (m_dialogs
.contains(widget
)) {
432 widget
->activateWindow(); //gives keyboard focus
433 //FIXME when returning from a subdialog to another dialog,
434 //kbd focus is broken, only esc/enter work
437 XChangeProperty(QX11Info::display(), widget
->effectiveWinId(), tag
, tag
, 8, PropModeReplace
, &data
, 1);
438 kDebug() << "tagged" << widget
<< widget
->effectiveWinId() << (data
!= 0);
444 void PlasmaApp::dialogDestroyed(QObject
*obj
)
446 m_dialogs
.removeAll(qobject_cast
<QWidget
*>(obj
));
447 if (m_dialogs
.isEmpty()) {
449 //this makes qactions work again
450 m_view
->activateWindow();
452 /*} else { failed attempt to fix kbd input after a subdialog closes
453 QWidget *top = m_dialogs.last();
454 top->activateWindow();
459 void PlasmaApp::hideDialogs()
461 foreach (QWidget
*w
, m_dialogs
) {
464 //FIXME where does the focus go?
467 void PlasmaApp::showDialogs()
469 foreach (QWidget
*w
, m_dialogs
) {
472 //FIXME where does the focus go?
475 void PlasmaApp::createConfigurationInterface(KConfigDialog
*parent
)
477 QWidget
*widget
= new QWidget();
479 parent
->setButtons( KDialog::Ok
| KDialog::Cancel
| KDialog::Apply
);
480 parent
->addPage(widget
, parent
->windowTitle());
481 connect(parent
, SIGNAL(applyClicked()), this, SLOT(configAccepted()));
482 connect(parent
, SIGNAL(okClicked()), this, SLOT(configAccepted()));
483 ui
.activeSlider
->setValue(m_activeOpacity
* 10);
484 ui
.idleSlider
->setValue(m_idleOpacity
* 10);
485 //TODO tell non-composite users they won't get anything other than 0 or 100%
486 //and generally prettify the UI
489 void PlasmaApp::configAccepted()
491 setActiveOpacity(ui
.activeSlider
->value() / 10.0);
492 setIdleOpacity(ui
.idleSlider
->value() / 10.0);
495 void PlasmaApp::lock()
497 if (corona() && corona()->immutability() == Plasma::Mutable
) {
499 corona()->setImmutability(Plasma::UserImmutable
);
503 #include "plasmaapp.moc"