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
);
247 m_corona
->requestConfigSync();
250 void PlasmaApp::setIdleOpacity(qreal opacity
)
252 if (qFuzzyCompare(opacity
, m_idleOpacity
)) {
255 m_idleOpacity
= opacity
;
256 KConfigGroup
cg(KGlobal::config(), "General");
257 cg
.writeEntry("idleOpacity", opacity
);
258 m_corona
->requestConfigSync();
261 qreal
PlasmaApp::activeOpacity() const
263 return m_activeOpacity
;
266 qreal
PlasmaApp::idleOpacity() const
268 return m_idleOpacity
;
272 void PlasmaApp::setActive(bool activate
)
279 m_view
->setWindowOpacity(m_activeOpacity
);
281 m_view
->containment()->openToolBox();
282 } else if (m_view
->isVisible()) {
283 if (qFuzzyCompare(m_idleOpacity
+ qreal(1.0), qreal(1.0))) {
288 m_view
->setWindowOpacity(m_idleOpacity
);
289 m_view
->containment()->closeToolBox();
292 if (m_idleOpacity
> 0) {
293 m_view
->setWindowOpacity(m_idleOpacity
);
300 void PlasmaApp::adjustSize(int screen
)
305 //FIXME someone needs to tell us what size to use if we've got >1 screen
306 QDesktopWidget
*desktop
= QApplication::desktop();
307 QRect geom
= desktop
->screenGeometry(0);
308 m_view
->setGeometry(geom
);
311 void PlasmaApp::syncConfig()
313 KGlobal::config()->sync();
316 Plasma::Corona
* PlasmaApp::corona()
319 m_corona
= new SaverCorona(this);
320 connect(m_corona
, SIGNAL(containmentAdded(Plasma::Containment
*)),
321 this, SLOT(createView(Plasma::Containment
*)));
322 connect(m_corona
, SIGNAL(configSynced()), SLOT(syncConfig()));
323 //kDebug() << "connected to containmentAdded";
325 foreach (DesktopView *view, m_desktops) {
326 connect(c, SIGNAL(screenOwnerChanged(int,int,Plasma::Containment*)),
327 view, SLOT(screenOwnerChanged(int,int,Plasma::Containment*)));
330 m_corona
->setItemIndexMethod(QGraphicsScene::NoIndex
);
331 m_corona
->initializeLayout();
333 //kDebug() << "layout should exist";
340 bool PlasmaApp::hasComposite()
345 //I think we need this for when the corona loads the default setup
346 //but maybe something simpler would suffice
347 void PlasmaApp::createView(Plasma::Containment
*containment
)
349 kDebug() << "Containment name:" << containment
->name()
350 << "| type" << containment
->containmentType()
351 << "| screen:" << containment
->screen()
352 << "| geometry:" << containment
->geometry()
353 << "| zValue:" << containment
->zValue();
356 // we already have a view for this screen
360 kDebug() << "creating a view for" << containment
->screen() << "and we have"
361 << QApplication::desktop()->numScreens() << "screens";
363 // we have a new screen. neat.
364 m_view
= new SaverView(containment
, 0);
366 connect(m_corona, SIGNAL(screenOwnerChanged(int,int,Plasma::Containment*)),
367 view, SLOT(screenOwnerChanged(int,int,Plasma::Containment*)));
369 //FIXME is this the right geometry for multi-screen?
370 m_view
->setGeometry(QApplication::desktop()->screenGeometry(containment
->screen()));
372 //FIXME why do I get BadWindow?
373 //unsigned char data = VIEW;
374 //XChangeProperty(QX11Info::display(), m_view->effectiveWinId(), tag, tag, 8, PropModeReplace, &data, 1);
376 connect(containment
, SIGNAL(locked()), SLOT(hideDialogs()));
377 connect(containment
, SIGNAL(locked()), m_view
, SLOT(disableSetupMode()));
378 connect(containment
, SIGNAL(unlocked()), SLOT(showDialogs()));
379 connect(containment
, SIGNAL(configureRequested(Plasma::Containment
*)),
380 this, SLOT(configureContainment(Plasma::Containment
*)));
382 connect(m_view
, SIGNAL(hidden()), SLOT(lock()));
383 connect(m_view
, SIGNAL(hidden()), SIGNAL(hidden()));
385 kDebug() << "view created";
388 void PlasmaApp::setup(bool setupMode
)
390 kDebug() << setupMode
;
392 kDebug() << "too soon!!";
397 m_view
->enableSetupMode();
398 if (m_corona
->immutability() == Plasma::UserImmutable
) {
399 m_corona
->setImmutability(Plasma::Mutable
);
403 kDebug() << "checking lockprocess is still around";
404 QDBusInterface
lockprocess("org.kde.screenlocker", "/LockProcess",
405 "org.kde.screenlocker.LockProcess", QDBusConnection::sessionBus(), this);
406 if (lockprocess
.isValid()) {
407 kDebug() << "success!";
410 kDebug() << "bailing out";
411 qApp
->quit(); //this failed once. why?
416 bool PlasmaApp::eventFilter(QObject
*obj
, QEvent
*event
)
418 if (event
->type() == QEvent::Show
) {
419 //apparently this means we created a new window
420 //so, add a tag to prove it's our window
421 //FIXME using the show event means we tag on every show, not just the first.
422 //harmless but kinda wasteful.
423 QWidget
*widget
= qobject_cast
<QWidget
*>(obj
);
424 if (widget
&& widget
->isWindow() && !(qobject_cast
<QDesktopWidget
*>(widget
) ||
425 widget
->testAttribute(Qt::WA_DontShowOnScreen
))) {
426 unsigned char data
= 0;
427 if (qobject_cast
<SaverView
*>(widget
)) {
429 } else if (m_dialogs
.contains(widget
)) {
432 Qt::WindowFlags oldFlags
= widget
->windowFlags();
433 Qt::WindowFlags newFlags
= oldFlags
| Qt::X11BypassWindowManagerHint
;
434 if (oldFlags
!= newFlags
) {
435 //now we're *really* fucking with things
436 //we force-disable window management and frames to cut off access to wm-y stuff
437 //and to make it easy to check the tag (frames are a pain)
438 kDebug() << "!!!!!!!setting flags on!!!!!" << widget
;
439 if (qobject_cast
<Plasma::Dialog
*>(widget
)) {
440 //this is a terrible horrible hack that breaks extenders but it mostly works
441 //weird thing is, it sometimes makes the calendar popup too small.
442 newFlags
= Qt::Popup
;
444 //plasmadialogs can't handle direct input
445 //but configdialogs need it
446 m_dialogs
.append(widget
);
447 connect(widget
, SIGNAL(destroyed(QObject
*)), SLOT(dialogDestroyed(QObject
*)));
449 widget
->setWindowFlags(newFlags
);
450 widget
->show(); //setting the flags hid it :(
451 //qApp->setActiveWindow(widget); //gives kbd but not mouse events
452 //kDebug() << "parent" << widget->parentWidget();
453 //FIXME why can I only activate these dialogs from this exact line?
454 widget
->activateWindow(); //gives keyboard focus
455 return false; //we'll be back when we get the new show event
457 widget
->activateWindow(); //gives keyboard focus
461 XChangeProperty(QX11Info::display(), widget
->effectiveWinId(), tag
, tag
, 8, PropModeReplace
, &data
, 1);
462 kDebug() << "tagged" << widget
<< widget
->effectiveWinId() << "as" << data
;
468 void PlasmaApp::dialogDestroyed(QObject
*obj
)
470 m_dialogs
.removeAll(qobject_cast
<QWidget
*>(obj
));
471 if (m_dialogs
.isEmpty()) {
473 //this makes qactions work again
474 m_view
->activateWindow();
476 /*} else { failed attempt to fix kbd input after a subdialog closes
477 QWidget *top = m_dialogs.last();
478 top->activateWindow();
483 void PlasmaApp::hideDialogs()
485 foreach (QWidget
*w
, m_dialogs
) {
489 m_view
->hideAppletBrowser();
491 //FIXME where does the focus go?
494 void PlasmaApp::showDialogs()
496 foreach (QWidget
*w
, m_dialogs
) {
499 //FIXME where does the focus go?
502 void PlasmaApp::configureContainment(Plasma::Containment
*containment
)
508 if (m_configDialog
) {
509 m_configDialog
->reloadConfig();
511 const QSize resolution
= QApplication::desktop()->screenGeometry(containment
->screen()).size();
513 m_configDialog
= new BackgroundDialog(resolution
, containment
, m_view
);
514 m_configDialog
->setAttribute(Qt::WA_DeleteOnClose
);
515 connect(m_configDialog
, SIGNAL(destroyed(QObject
*)),
516 this, SLOT(configDialogRemoved(QObject
*)));
519 m_configDialog
->show();
522 void PlasmaApp::configDialogRemoved(QObject
* dialog
)
527 void PlasmaApp::lock()
529 if (corona() && corona()->immutability() == Plasma::Mutable
) {
532 m_view
->disableSetupMode();
534 corona()->setImmutability(Plasma::UserImmutable
);
538 void PlasmaApp::quit()
543 #include "plasmaapp.moc"