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 <ksmserver_interface.h>
54 #include <Plasma/Containment>
55 #include <Plasma/Theme>
56 #include <Plasma/Dialog>
58 #include "appadaptor.h"
59 #include "savercorona.h"
60 #include "saverview.h"
61 #include "backgrounddialog.h"
65 #include <X11/extensions/Xrender.h>
67 Atom tag
; //FIXME should this be a member var or what?
68 const unsigned char DIALOG
= 1; //FIXME this is really bad code
69 const unsigned char VIEW
= 2;
72 Colormap colormap
= 0;
74 bool composite
= false;
78 dpy
= XOpenDisplay(0); // open default display
80 kError() << "Cannot connect to the X server" << endl
;
83 if( qgetenv( "KDE_SKIP_ARGB_VISUALS" ) == "1" )
86 int screen
= DefaultScreen(dpy
);
87 int eventBase
, errorBase
;
89 if (XRenderQueryExtension(dpy
, &eventBase
, &errorBase
)) {
92 templ
.screen
= screen
;
94 templ
.c_class
= TrueColor
;
95 XVisualInfo
*xvi
= XGetVisualInfo(dpy
,
96 VisualScreenMask
| VisualDepthMask
| VisualClassMask
,
98 for (int i
= 0; i
< nvi
; ++i
) {
99 XRenderPictFormat
*format
= XRenderFindVisualFormat(dpy
, xvi
[i
].visual
);
100 if (format
->type
== PictTypeDirect
&& format
->direct
.alphaMask
) {
101 visual
= xvi
[i
].visual
;
102 colormap
= XCreateColormap(dpy
, RootWindow(dpy
, screen
), visual
, AllocNone
);
109 composite
= KWindowSystem::compositingActive() && colormap
;
111 kDebug() << (colormap
? "Plasma has an argb visual" : "Plasma lacks an argb visual") << visual
<< colormap
;
112 kDebug() << ((KWindowSystem::compositingActive() && colormap
) ? "Plasma can use COMPOSITE for effects"
113 : "Plasma is COMPOSITE-less") << "on" << dpy
;
116 PlasmaApp
* PlasmaApp::self()
120 return new PlasmaApp(dpy
, visual
? Qt::HANDLE(visual
) : 0, colormap
? Qt::HANDLE(colormap
) : 0);
123 return qobject_cast
<PlasmaApp
*>(kapp
);
126 PlasmaApp::PlasmaApp(Display
* display
, Qt::HANDLE visual
, Qt::HANDLE colormap
)
127 : KUniqueApplication(display
, visual
, colormap
),
132 //load translations for libplasma
133 KGlobal::locale()->insertCatalog("libplasma");
134 KGlobal::locale()->insertCatalog("plasma-shells-common");
136 new AppAdaptor(this);
137 QDBusConnection::sessionBus().registerObject("/App", this);
139 //FIXME this is probably totally invalid
140 // Enlarge application pixmap cache
141 // Calculate the size required to hold background pixmaps for all screens.
142 // Add 10% so that other (smaller) pixmaps can also be cached.
144 QDesktopWidget
*desktop
= QApplication::desktop();
145 for (int i
= 0; i
< desktop
->numScreens(); i
++) {
146 QRect geometry
= desktop
->screenGeometry(i
);
147 cacheSize
+= 4 * geometry
.width() * geometry
.height() / 1024;
149 cacheSize
+= cacheSize
/ 10;
151 // Calculate the size of physical system memory; _SC_PHYS_PAGES *
152 // _SC_PAGESIZE is documented to be able to overflow 32-bit integers,
153 // so apply a 10-bit shift. FreeBSD 6-STABLE doesn't have _SC_PHYS_PAGES
154 // (it is documented in FreeBSD 7-STABLE as "Solaris and Linux extension")
155 // so use sysctl in those cases.
156 #if defined(_SC_PHYS_PAGES)
157 int memorySize
= sysconf(_SC_PHYS_PAGES
);
158 memorySize
*= sysconf(_SC_PAGESIZE
) / 1024;
162 size_t size
= sizeof(sysctlbuf
);
164 // This could actually use hw.physmem instead, but I can't find
165 // reliable documentation on how to read the value (which may
166 // not fit in a 32 bit integer).
167 if (!sysctlbyname("vm.stats.vm.v_page_size", sysctlbuf
, &size
, NULL
, 0)) {
168 memorySize
= sysctlbuf
[0] / 1024;
169 size
= sizeof(sysctlbuf
);
170 if (!sysctlbyname("vm.stats.vm.v_page_count", sysctlbuf
, &size
, NULL
, 0)) {
171 memorySize
*= sysctlbuf
[0];
178 static int mib
[] = { CTL_HW
, HW_PHYSMEM
};
180 len
= sizeof(memorySize
);
181 sysctl(mib
, 2, &memorySize
, &len
, NULL
, 0);
184 // If you have no suitable sysconf() interface and are not FreeBSD,
185 // then you are out of luck and get a compile error.
188 // Increase the pixmap cache size to 1% of system memory if it isn't already
189 // larger so as to maximize cache usage. 1% of 1GB ~= 10MB.
190 if (cacheSize
< memorySize
/ 100) {
191 cacheSize
= memorySize
/ 100;
194 kDebug() << "Setting the pixmap cache size to" << cacheSize
<< "kilobytes";
195 QPixmapCache::setCacheLimit(cacheSize
);
197 KConfigGroup
cg(KGlobal::config(), "General");
198 Plasma::Theme::defaultTheme()->setFont(cg
.readEntry("desktopFont", font()));
199 m_activeOpacity
= cg
.readEntry("activeOpacity", 1.0);
200 m_idleOpacity
= cg
.readEntry("idleOpacity", 1.0);
202 if (cg
.readEntry("forceNoComposite", false)) {
206 //we have to keep an eye on created windows
207 tag
= XInternAtom(QX11Info::display(), "_KDE_SCREENSAVER_OVERRIDE", False
);
208 qApp
->installEventFilter(this);
210 // this line initializes the corona.
213 connect(QApplication::desktop(), SIGNAL(resized(int)), SLOT(adjustSize(int)));
214 connect(this, SIGNAL(aboutToQuit()), this, SLOT(cleanup()));
216 setup(KCmdLineArgs::parsedArgs()->isSet("setup"));
219 PlasmaApp::~PlasmaApp()
223 void PlasmaApp::cleanup()
226 m_corona
->saveLayout();
232 KGlobal::config()->sync();
235 void PlasmaApp::setActiveOpacity(qreal opacity
)
237 if (qFuzzyCompare(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 (qFuzzyCompare(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::setActive(bool activate
)
277 m_view
->setWindowOpacity(m_activeOpacity
);
279 m_view
->containment()->openToolBox();
280 } else if (m_view
->isVisible()) {
281 if (qFuzzyCompare(m_idleOpacity
+ qreal(1.0), qreal(1.0))) {
286 m_view
->setWindowOpacity(m_idleOpacity
);
287 m_view
->containment()->closeToolBox();
290 if (m_idleOpacity
> 0) {
291 m_view
->setWindowOpacity(m_idleOpacity
);
298 void PlasmaApp::adjustSize(int screen
)
303 //FIXME someone needs to tell us what size to use if we've got >1 screen
304 QDesktopWidget
*desktop
= QApplication::desktop();
305 QRect geom
= desktop
->screenGeometry(0);
306 m_view
->setGeometry(geom
);
309 Plasma::Corona
* PlasmaApp::corona()
312 m_corona
= new SaverCorona(this);
313 connect(m_corona
, SIGNAL(containmentAdded(Plasma::Containment
*)),
314 this, SLOT(createView(Plasma::Containment
*)));
315 //kDebug() << "connected to containmentAdded";
317 foreach (DesktopView *view, m_desktops) {
318 connect(c, SIGNAL(screenOwnerChanged(int,int,Plasma::Containment*)),
319 view, SLOT(screenOwnerChanged(int,int,Plasma::Containment*)));
322 m_corona
->setItemIndexMethod(QGraphicsScene::NoIndex
);
323 m_corona
->initializeLayout();
325 //kDebug() << "layout should exist";
332 bool PlasmaApp::hasComposite()
337 //I think we need this for when the corona loads the default setup
338 //but maybe something simpler would suffice
339 void PlasmaApp::createView(Plasma::Containment
*containment
)
341 kDebug() << "Containment name:" << containment
->name()
342 << "| type" << containment
->containmentType()
343 << "| screen:" << containment
->screen()
344 << "| geometry:" << containment
->geometry()
345 << "| zValue:" << containment
->zValue();
348 // we already have a view for this screen
352 kDebug() << "creating a view for" << containment
->screen() << "and we have"
353 << QApplication::desktop()->numScreens() << "screens";
355 // we have a new screen. neat.
356 m_view
= new SaverView(containment
, 0);
358 connect(m_corona, SIGNAL(screenOwnerChanged(int,int,Plasma::Containment*)),
359 view, SLOT(screenOwnerChanged(int,int,Plasma::Containment*)));
361 //FIXME is this the right geometry for multi-screen?
362 m_view
->setGeometry(QApplication::desktop()->screenGeometry(containment
->screen()));
364 //FIXME why do I get BadWindow?
365 //unsigned char data = VIEW;
366 //XChangeProperty(QX11Info::display(), m_view->effectiveWinId(), tag, tag, 8, PropModeReplace, &data, 1);
368 connect(containment
, SIGNAL(locked()), SLOT(hideDialogs()));
369 connect(containment
, SIGNAL(locked()), m_view
, SLOT(disableSetupMode()));
370 connect(containment
, SIGNAL(unlocked()), SLOT(showDialogs()));
371 connect(containment
, SIGNAL(configureRequested(Plasma::Containment
*)),
372 this, SLOT(configureContainment(Plasma::Containment
*)));
374 connect(m_view
, SIGNAL(hidden()), SLOT(lock()));
375 connect(m_view
, SIGNAL(hidden()), SIGNAL(hidden()));
377 kDebug() << "view created";
380 void PlasmaApp::setup(bool setupMode
)
382 kDebug() << setupMode
;
384 kDebug() << "too soon!!";
389 m_view
->enableSetupMode();
390 if (m_corona
->immutability() == Plasma::UserImmutable
) {
391 m_corona
->setImmutability(Plasma::Mutable
);
395 kDebug() << "checking lockprocess is still around";
396 QDBusInterface
lockprocess("org.kde.krunner_lock", "/LockProcess",
397 "org.kde.krunner_lock.LockProcess", QDBusConnection::sessionBus(), this);
398 if (lockprocess
.isValid()) {
399 kDebug() << "success!";
402 kDebug() << "bailing out";
403 qApp
->quit(); //this failed once. why?
408 bool PlasmaApp::eventFilter(QObject
*obj
, QEvent
*event
)
410 if (event
->type() == QEvent::Show
) {
411 //apparently this means we created a new window
412 //so, add a tag to prove it's our window
413 //FIXME using the show event means we tag on every show, not just the first.
414 //harmless but kinda wasteful.
415 QWidget
*widget
= qobject_cast
<QWidget
*>(obj
);
416 if (widget
&& widget
->isWindow() && !(qobject_cast
<QDesktopWidget
*>(widget
) ||
417 widget
->testAttribute(Qt::WA_DontShowOnScreen
))) {
418 unsigned char data
= 0;
419 if (qobject_cast
<SaverView
*>(widget
)) {
421 } else if (m_dialogs
.contains(widget
)) {
424 Qt::WindowFlags oldFlags
= widget
->windowFlags();
425 Qt::WindowFlags newFlags
= oldFlags
| Qt::X11BypassWindowManagerHint
;
426 if (oldFlags
!= newFlags
) {
427 //now we're *really* fucking with things
428 //we force-disable window management and frames to cut off access to wm-y stuff
429 //and to make it easy to check the tag (frames are a pain)
430 kDebug() << "!!!!!!!setting flags on!!!!!" << widget
;
431 if (qobject_cast
<Plasma::Dialog
*>(widget
)) {
432 //this is a terrible horrible hack that breaks extenders but it mostly works
433 //weird thing is, it sometimes makes the calendar popup too small.
434 newFlags
= Qt::Popup
;
436 //plasmadialogs can't handle direct input
437 //but configdialogs need it
438 m_dialogs
.append(widget
);
439 connect(widget
, SIGNAL(destroyed(QObject
*)), SLOT(dialogDestroyed(QObject
*)));
441 widget
->setWindowFlags(newFlags
);
442 widget
->show(); //setting the flags hid it :(
443 //qApp->setActiveWindow(widget); //gives kbd but not mouse events
444 //kDebug() << "parent" << widget->parentWidget();
445 //FIXME why can I only activate these dialogs from this exact line?
446 widget
->activateWindow(); //gives keyboard focus
447 return false; //we'll be back when we get the new show event
449 widget
->activateWindow(); //gives keyboard focus
453 XChangeProperty(QX11Info::display(), widget
->effectiveWinId(), tag
, tag
, 8, PropModeReplace
, &data
, 1);
454 kDebug() << "tagged" << widget
<< widget
->effectiveWinId() << "as" << data
;
460 void PlasmaApp::dialogDestroyed(QObject
*obj
)
462 m_dialogs
.removeAll(qobject_cast
<QWidget
*>(obj
));
463 if (m_dialogs
.isEmpty()) {
465 //this makes qactions work again
466 m_view
->activateWindow();
468 /*} else { failed attempt to fix kbd input after a subdialog closes
469 QWidget *top = m_dialogs.last();
470 top->activateWindow();
475 void PlasmaApp::hideDialogs()
477 foreach (QWidget
*w
, m_dialogs
) {
481 m_view
->hideAppletBrowser();
483 //FIXME where does the focus go?
486 void PlasmaApp::showDialogs()
488 foreach (QWidget
*w
, m_dialogs
) {
491 //FIXME where does the focus go?
494 void PlasmaApp::configureContainment(Plasma::Containment
*containment
)
500 if (m_configDialog
) {
501 m_configDialog
->reloadConfig();
503 const QSize resolution
= QApplication::desktop()->screenGeometry(containment
->screen()).size();
505 m_configDialog
= new BackgroundDialog(resolution
, containment
, m_view
);
506 m_configDialog
->setAttribute(Qt::WA_DeleteOnClose
);
507 connect(m_configDialog
, SIGNAL(destroyed(QObject
*)),
508 this, SLOT(configDialogRemoved(QObject
*)));
511 m_configDialog
->show();
514 void PlasmaApp::configDialogRemoved(QObject
* dialog
)
519 void PlasmaApp::lock()
521 if (corona() && corona()->immutability() == Plasma::Mutable
) {
524 m_view
->disableSetupMode();
526 corona()->setImmutability(Plasma::UserImmutable
);
530 void PlasmaApp::quit()
535 #include "plasmaapp.moc"